跟我一起Django - 03 Django工程师一定要知道的Python

目录:

  1. Python技术就是Django技术
  2. Python交互解释器python&iPython
  3. Python语法基础,注释、变量和赋值,符号
  4. Python标准类型,布尔,数字,字符串str,列表list,元组tuple,字典dict
  5. Python流程控制
  6. Python异常处理
  7. 文件
  8. 函数
  9. 面向对象编程
  10. 正则表达式
  11. 常见错误
  12. 代码风格
  13. 总结

参照内容:经典Django教程《Python+Django+Web开发指南》

重点概念:

交互解释器
tab还是空格
对象的布尔值
经典除法 真正除法
内置数字工厂
序列
列表,字符串,元组
列表推导式
生成器表达式
不可变
字符串指示符
内置序列函数
表达式 语句
匿名函数 lambda
垃圾自动回收
动态实例属性

3.1 Python技术就是Django技术

Django提供了一个高级的框架,用它只需要很少代码就可以完成一个Web应用。

Django使用Python写成的,Python是一门OOP的开发语言,同时兼具系统语言(C、C++、Java)的强大和脚本语言(Ruby和VB)的灵活迅速。

本节主要总结Django工程师一定要知道的Python概念,而不是一份通用的Python教程。

http://www.cnblogs.com/ganiks

3.2 入门:Python交互解释器

Python Shell中,不用去创建和保存一个源文件,就可以直接把代码拿来测试。

>>> print 'Hello world!'
Hello World!

来看一个复杂的例子:

ganiks.liu@MAMIS-GAIKS-LIU /E/mysite (master)
$ python
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> for word in ['capitalize', 'these', 'words']:
... print word.upper()
  File "<stdin>", line 2
    print word.upper()
        ^
IndentationError: expected an indented block
>>> for word in ['capitalize', 'these', 'words']:
...     print word.upper()
...
CAPITALIZE
THESE
WORDS
>>> for i in rang(0,5):
...     print i
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'rang' is not defined
>>> for i in range(0,5):
...     print i
...
0
1
2
3
4
>>>

python语法中重要的一个方面就是没有区分代码库的花括号{ }, 而是用对齐来替代

更高级的交互解释器iPython

iPython的能力完全是另一个数量级上的解释器。它提供了系统shell访问、命令行技术、自动对齐、命令行历史等许多特性。

在解释器里使用Django

要是能在Python的解释器中调试Django应用是不是很酷?

  • 假如你只是按照常规启动解释器并且试着导入Django模块,你只能得到一个
    DJANGO_SETTINGS_MODULE没有设置的错误。为了方便起见,Django提供了一个manage.py
    shell命令,能进行一些必要的环境设置来避免
  • 如果安装了 iPython, manage.py shell 会默认使用它。
  • 如果你安装了iPython,又希望能使用标准Python解释器的话,可以运行manage.py
    shell plain

3.3 Python基础

  • 注释 —— #
  • 变量和赋值 —— 除非你想解释器询问更多信息,否则你不可能百分百确定变量在任何给定的时刻是什么类型的对象

    $ python
    Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> foo = 'bar'
    >>> foo
    'bar'
    >>> type(foo)
    <type 'str'>
    >>> foo = 1
    >>> type(foo)
    <type 'int'>
    >>>
  • 运算符
  • 避免使用的符号
  • 结尾的 ;
  • $
  • 条件语句的()

Python的作者:只有干净和容易阅读的代码才能避免混乱

3.4 Python标准类型

  • 标量(scalar)
  • 文字量(literal) 比如数字/字符串
  • 多个Python对象组织在一起的容器、数据结构

3.4.1 对象的布尔值

所有的Python值都可以表示为布尔值,这个的用处在哪里?
在于if和while语句里面做条件判断~

>>> download_complete = False
>>> bool(download_complete)
False
>>> bool(-1.2)
True
>>> bool("")
False
>>> bool([None])
True
>>> bool([None, 0])
True
>>> bool([])
False
>>>

3.4.2 数字

  • int 带符号整数 **python只有一种整数类型**
  • float 双精度浮点数
  • complex 复数
>>> type(3.75)
<type 'float'>
>>> type(0377)
<type 'int'>
>>> type(0xE8C6)
<type 'int'>
>>> type(2+2j)
<type 'complex'>

3.4.2.1 数字运算符

  • + - *
  • / floor division 经典除法(整数相除结果取整,浮点相除结果取浮点)
  • // true division 真正的除法 (统统取整数结果)
  • % 取模
  • ** 指数运算
  • & | ~ ^ >> << &= <<=

关于 / //

In [8]: 1/2
Out[8]: 0

In [9]: 1.0/2
Out[9]: 0.5

In [10]: 1.0/2.0
Out[10]: 0.5

In [11]: 1/2.0
Out[11]: 0.5

In [12]: 1//2
Out[12]: 0

In [13]: 1.0//2
Out[13]: 0.0

In [14]: 1.0//2.0
Out[14]: 0.0

In [15]: 1//2.0
Out[15]: 0.0

In [16]: type(1//2.0)
Out[16]: float

In [17]: type(1//2)
Out[17]: int

3.4.2.2 内置数字工厂函数/类型转换

>>> int('123')
123
>>> int(45.67)
45
>>> round(1.15, 1)
1.1
>>> float(10)
10.0
>>> divmod(15, 6)
(2, 3)
>>> ord('a')
97
>>> chr(65)
'A'
>>>

3.4.3 序列和迭代

序列类型类似其他语言中的数组,不同的是:

  1. 数组是定长的,而序列长度可以伸缩
  2. 数组有相似的对象组合,可以包含不同类型的对象

常见的序列有:

  1. 列表 list [1, 2, 3] [123, 'foo', 3.14]
  2. 字符串 string 'python' '\n' '%s is number %d'
  3. 元组 tuple (45, 2.71) () ('need a comma even with just 1 item',)

像数组一样, 序列可以直接索引:

>>> s = 'Python'
>>> s[0]
'P'
>>> s[4]
'o'
>>> s[-1]
'n'

还可以一次索引多个元素,在这里成为序列切片

>>> s[1:4]
'yth'
>>> s[3:]
'hon'
>>> s[3:-1]
'ho'
>>> s[:]
'Python'
>>> str(s)
'Python'

切片语法适用于所有的序列,包括字符串、元组、列表

其他的序列操作符还有连接, 复制, 检查是否是成员

>>> 'Python and ' + 'Django are cool'
'Python and Django are cool'
>>> 'Python and ' 'Django are cool'
'Python and Django are cool'
>>> '-'*40
'----------------------------------------'
>>> 'an' in 'Django'
True
>>> 'xyz' in 'Django'
False
建避免在序列上使用 + 操作符,因为其效率不高
>>> 'foo'+'bar'
'foobar'
#建议使用下面两种方式
>>> '%s%s' % ('foo', 'bar')
'foobar'
>>> s=''
>>> s.join(['foo', 'bar'])
'foobar'

3.4.4 列表

>>> book=['python', 'develop', 8]
>>> book.append(2008)
>>> book.insert(1, 'web')
>>> book
['python', 'web', 'develop', 8, 2008]
>>> book[:3]
['python', 'web', 'develop']
>>> 'django' in book
False
>>> book.remove(8)
>>> book.pop(-1)
2008
>>> book
['python', 'web', 'develop']
>>> book*2
['python', 'web', 'develop', 'python', 'web', 'develop']
>>> book.extend(['with', 'django'])
>>> book
['python', 'web', 'develop', 'with', 'django']
>>> book.extend([])
>>> book.extend(['end'])
>>> book
['python', 'web', 'develop', 'with', 'django', 'end']
>>> book.sort()
>>> book
['develop', 'django', 'end', 'python', 'web', 'with']

这里注意一点跟string字符串的区别,列表的内置函数如sort, append, insert都是直接对对象进行修改,而没有返回值;字符串比如用upper方法则是返回了一个字符串。
这是因为字符串是不可改变的,而列表可变
python2.4以上版本提供了sorted和reversed方法可以接受一个列表作为参数并返回一个处理过的拷贝

>>> book=['c','b',4,1,'a',2,3]
>>> sbook = sorted(book)
>>> sbook
[1, 2, 3, 4, 'a', 'b', 'c']

列表推导式[ ] and 生成器表达式( )

>>> data = [x for x in range(10) if x%2 ==0]
>>> data
[0, 2, 4, 6, 8]
>>> data = (x for x in range(100) if x%2 ==0)
>>> data
<generator object <genexpr> at 0x00DCE710>

如果你的输入序列很大,最好用生成器表达式,节省内存

3.4.5 字符串

字符串不能修改,大小也不能改变
任何没有一个能修改现有的对象的方法,只是返回一个修改了的拷贝
到目前为止,已经有超过37个字符串方法

>>> s = 'Django is cool'
>>> words = s.split()
>>> words
['Django', 'is', 'cool']
>>> ' '.join(words)
'Django is cool'
>>> '::'.join(words)
'Django::is::cool'
>>> s.upper()
'DJANGO IS COOL'
>>> s
'Django is cool'
>>> s.upper().isupper()
True
>>> s.title()
'Django Is Cool'
>>> s.capitalize()
'Django is cool'
>>> s.count('o')
3
>>> s.find('go')
4
>>> s.find('xxxx')
-1
>>> s.startswith('Python')
False
>>> s.startswith('Django')
True
>>> s.replace('Django', 'Python')
'Python is cool'
>>>

字符串指示符 u r

>>> mystr = u'This is Unicode'
>>> mystr
u'This is Unicode'
>>> print mystr
This is Unicode

>>> filename = 'C:\temp\newfolder'
>>> filename
'C:\temp\newfolder'
>>> print filename
C:      emp
ewfolder
>>> filename = r'C:\temp\newfolder'
>>> print filename
C:\temp\newfolder

#为了保持一致,所有的正则表达式都要用raw指示符
>>> regex = "\w+@\w+\.\w+"
>>> print regex
\w+@\w+\.\w+
>>> regex = r"\w+@\w+\.\w+"
>>> print regex
\w+@\w+\.\w+

Unicode指示符

通常Python字符串都只含有很少字符,不足以显示更多的非英文字符。而Unicode是新型的字符集,拥有大量的编码方式,不存在这个显示。

>>> str = "中国"
>>> str
'\xd6\xd0\xb9\xfa'
>>> str = u"中国"
>>> str
u'\u4e2d\u56fd'
>>> str = r"中国"
>>> str
'\xd6\xd0\xb9\xfa'
>>> print str
中国
>>> str = u"中国"
>>> print str
中国
>>> str = "中国"
>>> print str
中国

字符串格式化操作符 adn 三引号操作符

>>> s = 'Django is cool'
>>> '%s is number %d' % (s[:6], 1)
'Django is number 1'
>>> hi = '''hi
... there'''
>>> hi
'hi\nthere'
>>> print hi
hi
there

展示一个高级应用的实例

>>> xml = '''
... <?xml version="1.0"?>
... <request version="%.1f">
...    <Data>
...        <Payload>%s</Payload>
...    </Data>
... </request>
... '''
>>> VERSION = 1.2
>>> payload = 'super top-secret info'
>>> print (xml %(VERSION, payload))

<?xml version="1.0"?>
<request version="1.2">
   <Data>
       <Payload>super top-secret info</Payload>
   </Data>
</request>

3.4.6 元组

表明上元组和列表只是用什么括号() []的区别,深层次的区别则要说道Python的对象模型。

列表允许改变值,元组不可以!(像字符串)所以元组没有方法。

但是元组也不单单是一个只读列表,元组的主要作用是作为参数传递给函数调用,或是从函数调用那里获取参数时, 保护其内容不被外部接口修改。

所以元组在前台的用武之地不大,但是在后台却很频繁使用,例如Django的配置文件中的admin选项、URLconf规则、settings.py配置。

!!! 单个元素的元组要求在最后“必须”跟一个逗号,元组是逗号决定的,而不是小括号。

>>> a = ("one", "two")
>>> a
('one', 'two')
>>> type(a)
<type 'tuple'>
>>> a[0]
'one'
>>> a = ('just-one')
>>> a
'just-one'
>>> type(a)
<type 'str'>
>>> c = ('just-one',)
>>> c
('just-one',)
>>> type(c)
<type 'tuple'>
>>> d = "just-one",
>>> d
('just-one',)
>>> type(d)
<type 'tuple'>

内置序列函数和工厂函数

  • str
  • list
  • tuple
  • len
  • max
  • min
  • range(1,10)
  • sorted
  • sum
  • any
  • zip
>>> a=("one","two")
>>> type(a)
<type 'tuple'>
>>> a
('one', 'two')
>>> len(a)
2
>>> max(a)
'two'
>>> sorted(a)
['one', 'two']
>>> sum(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> any(a)
True
>>> zip(a, a)
[('one', 'one'), ('two', 'two')]
>>> b=list(a)
>>> b
['one', 'two']
>>> c=str(a)
>>> c
"('one', 'two')"
>>> sum(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

3.4.7 字典

字典是Python中唯一的映射类型,可变的,无序的,大小可变的键值映射;别名散列表(hashes) 关联数组

虽然语法跟序列相似,但是用而不是下标来访问元素;而且定义的话使用的花括号{ }

字典的操作、方法和映射函数

  • keys
  • values
  • items
  • get
  • pop
  • update
>>> book = {'title':'Python Web Dev', 'year':2008}
>>> book
{'year': 2008, 'title': 'Python Web Dev'}
>>> type(book)
<type 'dict'>
>>> 'year' in book
True
>>> book.get('pub')
>>> book.get('pub', 'N/A')
'N/A'
>>> book['pub']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'pub'
>>> book['pub']='Addision Wesley'
>>> book['pub']
'Addision Wesley'
>>> book.get('pub', 'N/A')
'Addision Wesley'
>>> for key in book:
... print key, ':', book[key]
  File "<stdin>", line 2
    print key, ':', book[key]
        ^
IndentationError: expected an indented block
>>> for key in book:
...     print key, ':', book[key]
...
year : 2008
pub : Addision Wesley
title : Python Web Dev
>>> book.setdefault('price', 100.00)
100.0
>>> book
{'price': 100.0, 'year': 2008, 'pub': 'Addision Wesley', 'title': 'Python Web Dev'}

>>> del book['price']
>>> book
{'year': 2008, 'pub': 'Addision Wesley', 'title': 'Python Web Dev'}
>>> book['title']='Python Web Dev with Django'
>>> book
{'year': 2008, 'pub': 'Addision Wesley', 'title': 'Python Web Dev with Django'}
>>> len(book)
3

Django 中类似字典的数据类型,Request对象和Response对象

总结:列表list和字典dict是应用的最频繁的数据类型

3.5. Python流程控制

3.5.1 判断if elif else

data = raw_input("Enter y or n :")
if data[0] == 'y':
    print "you enter y"
elif data[0] == 'n' :
    print "you enter n"
else
    print "invalid"

3.5.2 循环 常用 for

>>> i = 0
>>> while i < 5:
...     print i
...     i += 1
...
0
1
2
3
4

更常用的是for循环,这个for循环更像是shell中的foreach,让你专注于问题本身而不担心计数变量。

for line in open('/tmp/some_file.txt')
    if 'error' in line:
        print line

还记得列表推导式吗?
data = [x for x in range(10) if x%2 ==0]

当然如果你还是要在循环的同时计数的话,可以使用内置函数enumerate(for循环自身没办法计数)

>>> for i, value in data:
...     print i
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> for i, value in data:
...     print value
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> for value in data:
...     print value
...
123
abc
3.14
>>> data = (123, 'abc', 3.14)
>>> for i, value in enumerate(data):
...     print i, value
...
0 123
1 abc
2 3.14
>>>

在Django的Model中使用 enumerate

特别是在用到choices参数的地方

STATUS_CHOICES = enumerate(("solid", "liquid"))

class IceCream(models.Model):
    falvor = models.CharField(max_length = 50)
    status = models.ItergerField(choices = STAUTS_CHOICES)

在数据库里, 你的IceCream的status值被保存为整数(0, 1, 2), 但是在Django的admin界面中显示的确实是文字标签。

这样利用数据库存储的效率高,而且???当出现字符无法按照预想顺序排序的时候也比较方便。

3.6. Python异常处理

Python的try-except语句跟其他语言的try-catch结构相似;

运行时发生异常的话, 解释器会找相应的处理语句handler;
要是在当前函数没有找到,会将异常传递到上层函数;
如果到最外层全局main还是没有找到,解释器会退出,同时打印traceback以便让用户找出错误。
异常处理可以处理从简单的单个异常到一系列的多个不同的异常类型。

try:
    f = open(filename, 'r')
except IOError, e:
    return False, str(e)

同一段处理语句也可以处理多个异常类型,只要放在一个元组中即可

try:
    process_some_data()
except (TypeError, ValueError), e:
    print "Error: you provide invalid data", e

或者为多个异常创建多个处理语句:(有点像swithc case break)

try:
    process_some_data()
except (TypeError, ValueError), e:
    print "ERROR: invalid data", e
except ArithmeticError, e:
    print "ERROR: some math error", e
except Exceptioin, e:
    print "ERROR: invalid data", e

finally子句

Python还提供了一个 finally自居,无论错误是不是发生,这些代码都必须运行,比如关闭文件,释放锁, 把数据库连接返回给连接池。

try:
    get_mutex()
    do_some_stuff()
finally:
    free_mutex()

自Python2.5之后,try-finally 可以跟 except 一起使用。

try:
    get_mutex()
    do_some_stuff()
except (IndexError, KeyError, AttributeError), e:
    log("ERORR:...")
finally
    free_mutex()

用raise抛出异常

def foo(must_be_positive_int)
    if not isinstance(must_be_positive_int)
        raise TypeError("ERROR foo(): must pass in an integer")
    if must_be_positive_int < 1:
        raise ValueError("ERROR foo(): ...")

常见异常类型

  • AssertionError
  • AttributeError
  • IOError
  • ImportError
  • IndentationError
  • IndexError
  • KeyError
  • Keyboardinterrupt
  • NameError
  • SyntaxError
  • TypeError
  • UnboundLocalError
  • ValueError

more refert to : https://docs.python.org/2.7/library/exceptions.html#module-exceptions

3.7. 文件

>>> f = open('test.txt', 'w')
>>> f.write('foo\n')
>>> f.write('bar\n')
>>> f.close()
>>> dir
<built-in function dir>
>>> f = open('test.txt', 'r')
>>> for line in f:
...     print line.rstrip()
...
foo
bar

>>> f = open('test.txt', 'r')
>>> for line in f:
...     print line
...
foo

bar

除了read 和 write 方法还有 readlines 和 writelines 方法,让文件跟列表list打交道

>>> f = open('test.txt', 'r')
>>> f.read()
'foo\nbar\n'
>>> f = open('test.txt', 'r')
>>> f.readlines()
['foo\n', 'bar\n']

>>> f = open('test.txt', 'w')
>>> f.writelines(['foo', 'bar'])
>>> f = open('test.txt', 'r')
>>> f.read()
'foobar'

3.8. 函数(重点

  • 声明和调用函数
  • (函数调用里的)关键字参数
  • (函数签名里的)默认参数
  • 函数式first-class的对象
  • 匿名函数和lambda
  • (函数调用里的)参数容器
  • (函数签名里的)变长参数
  • 装饰器

3.8.1 声明和调用函数

>>> def foo(x):
...     print x
...
>>> foo(123)
123
>>> import httplib
>>> def check_web_server(host, port, path):
...     h = httplib.HTTPConnection(host, port)
...     h.request('GET', path)
...     resp = h.getresponse()
...     print 'HTTP Response:'
...     print '    status = ', resp.status
...     print '    reason = ', resp.reason
...     print 'HTTP Headers: '
...     for hdr in resp.getheaders():
...         print '    %s: %s' %hdr
...
>>> check_web_server('www.python.org', 80, '/')
HTTP Response:
    status =  301
    reason =  Moved Permanently
HTTP Headers:
    content-length: 0
    via: 1.1 varnish
    x-cache: MISS
    accept-ranges: bytes
    strict-transport-security: max-age=63072000; includeSubDomains
    server: Varnish
    retry-after: 0
    connection: close
    x-served-by: cache-ty67-TYO
    x-cache-hits: 0
    location: https://www.python.org/
    date: Wed, 24 Sep 2014 06:38:42 GMT
>>> check_web_server('www.baidu.com', 80, '/')
HTTP Response:
    status =  200
    reason =  OK
HTTP Headers:
    content-length: 14613
    bdqid: 0xa15e849b0000a0cc
    set-cookie: BAIDUID=C888FD3659C854691DC8B775D3D3C55E:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BDSVRTM=0; pat
h=/
    accept-ranges: bytes
    vary: Accept-Encoding
    bduserid: 0
    server: BWS/1.1
    last-modified: Wed, 03 Sep 2014 02:48:32 GMT
    connection: Keep-Alive
    pragma: no-cache
    cache-control: no-cache
    date: Wed, 24 Sep 2014 06:39:17 GMT
    p3p: CP=" OTI DSP COR IVA OUR IND COM "
    content-type: text/html
    bdpagetype: 1
>>>

3.8.2 (函数调用里的)关键字参数

调用的时候,我们需要记得函数定义中的参数顺序:host, port, path
那如果没有记得呢,可以用关键字参数,也是ok的

>>> check_web_server(path='/', port=80, host='www.baidu.com')

3.8.3 (函数签名里的)默认参数

定义函数的时候,Python同样支持默认参数的定义

>>> def check_web_server(host, port=80, path='/')

注意, 所有必须提供 的参数必须出现在可选参数之前,下面就是错误的

>>> def check_web_server(path, port=80, host) #INVALID

将列表和字典作为默认参数很危险

因为列表和字典是可变的,当他们持续穿过多个函数调用时

>>> def func(arg=[]):
...     arg.append(1)
...     print arg
...
>>> func()
[1]
>>> func()
[1, 1]
>>> func()
[1, 1, 1]
>>> func()
[1, 1, 1, 1]

3.8.4 函数是First-Class的对象

对象引用
在Python里面,你可把函数和方法当做和其他对象一样的使用

只有当我们要调用函数的时候才会加上小括号(), 在把它当做变量或者对象传来传去的时候,只需要用函数的名字就好。

这将引用bar和调用bar()区分开来

>>> def bar():
...     print "bar"
...
>>> bar()
bar
>>> baz = bar
>>> baz()
bar
>>>
>>>
>>> function_list = [bar, baz]
>>> for function in function_list:
...     function()
...
bar
bar

Django里的First-Class函数

常见的例子是 URLconf 文件中设置 Django View

from django.conf.urls.defaults import *
from myproject.myapp.views import listview

urlpatterns = patterns('',
    url(r'^list/', listview)
)

这里的 listview 作为了一个包含了函数对象被直接使用,而不是一个包含了函数名字的字符串

另一个利用函数对象的地方时 model field 里的默认参数。

import datetime

class DiaryEntry(models.Model):
    entry = models.TextField()
    date = models.DateField(default = datetime.date.today)

这里的 datetime.date.today后面没有() , 所以Django会在创建实例的时候才会调用函数而如果加上 (), 那么函数会在model定义的时候就调用,非我所想。

http://www.cnblogs.com/ganiks

3.8.5 匿名函数和lambda

表达式和语句 expression & statement

python 代码由 表达式和语句构成,并由python解释器负责自行。

表达式是一个值,他的结果一定是一个python对象

lambda args:表达式

举个例子,对比下3个不同的处理方式:
这里有一个代表人物的复杂对象列表,希望按照姓氏排序

>>> list_of_people = [{'name':'ganiks', 'last_name':'liu'}, {'name':'henry', 'last_name':'ge'}]
>>> list_of_people
[{'last_name': 'liu', 'name': 'ganiks'}, {'last_name': 'ge', 'name': 'henry'}]
>>> sorted(list_of_people, key = lambda person: person['last_name'])
[{'last_name': 'ge', 'name': 'henry'}, {'last_name': 'liu', 'name': 'ganiks'}]
#key期望的是一个函数对象,而lambda返回的正好是一个匿名函数
#这个例子简洁,目的明确
sorted(list_of_people, key = lambda person: person.last_name)

#这样也是等价的
#这个方便重复使用函数
def get_last_name(person):
    return person.last_name
sorted(list_of_people, key = get_last_name)

#不建议使用这种,我们希望lambda是一次性的函数定义
#不过,这进一步阐述了Python函数的First-Class的特性
get_last_name = lambda person: person.last_name
sorted(list_of_people, key = get_last_name)

Django里面的匿名lambda函数

Django里面用到匿名函数的地方不多,但有个地方非常合适,即“认证装饰器” authentication decorator,确认用户是否有足够权限访问页面;

将一个代表已登录用户的User对象传递给一个函数

@user_passes_test(lambda u: u.is_allowed_to_vote)
def vote(request):
    """ Process a user's vote"""

这个例子中调用了一个函数user_passes_test,接受了参数lambda u:u.is_allowed_to_vote之后返回 另一个“装饰器”;也就是说装饰器是 user_passes_test(lambda u:u.is_allowed_to_vote)

*args && **kwargs

无论是函数调用或者声明, *都代表元组(或者列表)出现, **代表字典出现。

3.8.6 (函数调用里的)参数容器

函数调用里的 * ** 让参数容器列表、元组、字典更优雅

def check_web_server(host, port, path)

check_web_server('localhost', 8000, '/damin/')

host_info = ('www.python.org', 80, '/')

checck_web_server(host_info[0], host_info[1], host_info[2])

*让代码干净和优雅
check_web_server(*host_info)

host_info = {'host': 'www.python.org', 'port': 80, 'path': '/'}

check_web_server(**host_info)

3.8.7 (函数签名里的)变长参数

函数签名里的 * ** 支持边长参数 varargs

在函数定义里使用变长参数时,参数的顺序是要求的;先是必须出现的参数,然后是由默认值的参数,最后才是变长参数

def check_web_server(host, port, path, *args, **args)

这个函数必须至少接受初始的三个参数,但是也能接受随后任何数目的参数或是关键字参数

还有一种全部由变长参数组成的所谓通用的Python方法签名

def f(*args, **kwargs):

#正确调用
f()
f(a, b, c)
f(a, b, foo=c, bar=d)

Django QuerySets里的 **kwargs: 动态创建ORM查询

Django数据库API查询经常包含关键字参数,比如:

bob_stories = Story.objects.filter(
    title_contains = "bob",
    subtitle_contains = "bob", 
    text_contains = "bob",
    byline_contains = "bob"
)

#用变长参数字典来让参数独立
bobargs = {
    'title_contains': "bob",
    'subtitle_contains': "bob", 
    'text_contains': "bob",
    'byline_contain': "bob"
}

bob_stories = Story.objects.filter(**bobargs)

#动态创建字典
bobargs = dict((f + '__contains', 'bob') for f in ('title', 'subtitle', 'text', 'byline'))
bob_stories = Story.objects.filter(**bobargs)

3.8.8 装饰器

装饰器是一种让你能改变或者说“装饰” 函数行为的机制,能让函数执行一些和原本设计不同,或是在原有基础上额外的操作。

装饰器也可以说是对函数的一个包装,这些额外的任务包括写日志、计时、过滤

python 里的一个被包裹或者被装饰的函数(对象)通常会被重新赋值给原来的名字

@deco
def foo():
    pass

等价于

foo = deco(foo)

再举个例子记录下函数调用的发生:

>>> def log(func):
...     def wrappedFunc():
...          print "**** %s() called" % func.__name__
...          return func()
...     return wrappedFunc
...
>>> @log
... def foo():
...     print "inside foo()"
...
>>> foo()
**** foo() called
inside foo()

再看看之前的一个例子
将一个代表已登录用户的User对象传递给一个函数

@user_passes_test(lambda u: u.is_allowed_to_vote)
def vote(request):
    """ Process a user's vote"""

这个例子中调用了一个函数user_passes_test,接受了参数lambda u:u.is_allowed_to_vote之后返回 另一个“装饰器”;也就是说装饰器是 user_passes_test(lambda u:u.is_allowed_to_vote)

它的逻辑是这样的:

@decomaker(deco_args)
def foo():
    pass

等价于

foo = decomaker(deco_args)(foo)

再比如应用多个装饰器:

@deco1(deco_args@)
@deco2
def foo():
    pass

为什么要用装饰器呢?
本质就是包裹函数,接受对象,修改并重新赋值给原来的变量;不同的是装饰器可以让你用一个简单的@符号来完成这一切动作

3.9. 面向对象编程

OOP的主要目标是在代码和显示问题之间提供一个合乎逻辑的映射关系,并且鼓励代码的重用和共享。

3.9.1 类的定义

class AddressBookEntry(object):
    version = 0.1

    def __init__(self, name, phone):
        self.name = name
        self.phone = phone

    def update_phone(self, phone):
       self.phone = phone
  • version这样的静态成员属于整个类的变了,可以再所有的实例之间共享
  • 方法的定义和函数的一点区别,每个方法都必须带上一个额外的self对象
  • python没有构造函数和析构函数,也没有 new free关键字

3.9.3 实例化

john = AddressBookEntry('Jone Doe', '400999999')
jane = AddressBookEntry('Jane Dey', '300293202')

>>> john.phone
'400999999'

>>> john.tattoo = 'Mom'

python中实例化不需要像其他语言一样new对象,而是可以直接调用函数一样即可

python用的是初始化程序initializer而不是构造函数 constructor,所以名字也是
__init__

在实例化一个对象的时候,不用传入self对象,python会自动为你传入self对象

PS: python还支持直接创造没有在类定义中声明的对象,比如上面例子中的tattoo

3.9.3 继承

class EmployeeAddressBookEntry(AddressBookEntry):
    def __init(self, name, phone, id, social):
        AddressBookEntry.__init__(self, name, phone)
        self.empid = id
        self.ssn = social

3.9.4 嵌套类

嵌套类的概念类似于创建装饰器的“嵌套函数”,即在类的内部定义类

class MyClass(object):
    class InnerClass:
        pass

Django中的嵌套类应用实例--Django Model

form django.db import models
from django.contrib import admin

class BlogPost(models.Model):
    title = models.CharField(max_length = 150)
    body = models.TextField()
    timestamp = models.DateTimeField()

class Meta:
    ordering = ('-timestamp',)

Django的数据model是从Django内置类 django.db.models.Model 继承而来的子类

3.10. 正则表达式

re.search 返回一个匹配对象Match,随后可以用这个对象的 group 或者 groups
方法获取匹配的模式;匹配失败时,返回的是None

额,这里用“匹配”有点不准确,确切的说是“搜索”,而“匹配”指的是整个字符串都要符合模式的描述

>>> import re
>>> m = re.search(r'foo', 'seafood')
>>> print m
<_sre.SRE_Match object at 0x00DAE4B8>
>>> m.group
<built-in method group of _sre.SRE_Match object at 0x00DAE4B8>
>>> m.group()
'foo'
>>> m = re.search(r'bar', 'seafood')
>>> print m
None
>>>
>>>
>>> import re
>>> m = re.match(r'foo', 'seafood')
>>> if m is not None: print m.group()
...
>>> print m
None
>>> if m is not None: print m.groups()
...
>>> print m
None
>>> m = re.search(r'foo', 'seafood')
>>> if m is not None: print m.group()
...
foo
>>> if m is not None: print m.groups()
...
()
>>> if m is not None: print m.group()
...
foo

3.11. 常见错误

3.11.1 单元素的元组

  • ()
  • (123, 'xyz', 3.14)
  • (1)
  • (1,)

3.11.2 模块

import random
print random.choice(range(10))

from random import choice
print choice(range(10))
  • 第一种方法将模块的名字设置为一个隐含的名字空间里的全局变量,这样可以像访问全局属性一样访问choice函数
  • 第二种方法直接把choice引入到全局名字控件,因此不再需要把这个属性当成是模块的成员,实际上我们也只拥有这个属性而已
  • 但是并不是说,第二种方式只引入了一个函数,而没有导入整个模块,其实是导入了整个模块的!!!所以没有性能的差异

3.11.3 能否重复导入模块??

python有导入模块和加载模块之分,一个模块可以被导入N次,但是只会被加载一次,无需担心额外消耗内存的问题。

3.11.4 Package

package是Python在文件系统上发布一组模块的一种方式,通过 __init__.py 实现

Phone/
    __init__.py
    util.py
    Voicedata/
        __init__.py
        posts.py
        Isdn.py
    Fax
        __init__.py
        G3.py
    Mobile
        __init__.py
        Anolog.py
        Digital.py

__init.__.py告诉Python解释器这些目录下的文件应该被当做是一个子package而不是普通文件,一般它们都是空文件,当然也可以做一些初始化的工作。

import Phone.Mobile.Analog
Phone.Mobile.Anolog.dial()

import Phone.Mobile.Analog as pma
pma.dial()

3.11.5 可改变性,到底Python是传引用 还是 传值?

在Python中跟一般不这么说,而是说 对象时可变或者不可变 mutable or immutable

对象有 类型,标示符,值 三个属性, 如果值 是可变的,那么这个对象就是可变的;

前面语法其实已经介绍过了,标量scalar类型(整数浮点数复数)、str、unicode字符串、元组是不可变的;其他的列表、字典、类、类实例都是可变的。

可变性是Python中重要的概念,这也解释了为什么Python要提供两种列表, list 和 tuple

可改变性如何影响方法调用

如果你调用的函数有任何修改一个可变对象的行为的话,通常它都是直接修改的,即直接修改数据结构而不是返回一个拷贝(返回None)

比如 list.sort, list.reverse, list.extend, dict.update都是直接修改

复制对象和可改变性

深拷贝和浅拷贝 deep copy & shallow copy

>>> from copy import deepcopy
>>> mylist2 = deepcopy(mylist)
>>> mylist2
[1, 'a', ['foo', 'bar']]
>>> mylist2[0] = 2
>>> mylist2[2][0] = 'biz'
>>> mylist2
[2, 'a', ['biz', 'bar']]
>>> mylist
[1, 'a', ['foo', 'bar']]

>>> mylist2 = list(mylist)
>>> mylist2[0] = 2
>>> mylist2[2][0] = 'biz'
>>> mylist
[1, 'a', ['biz', 'bar']]
>>> mylist2
[2, 'a', ['biz', 'bar']]

上面的例子中分别是一个深拷贝和浅拷贝的例子,先看看浅拷贝:

mylist中前两个对象时整数和字符串,所以mylist2会得到2个全新的整数和字符串对象,所以1=>2
但是mylist的第三个对象时列表,列表时可变的,所以mylist2得到的只是一个引用,所以
‘foo'=>'biz'影响到了mylist本身

而深拷贝就不会了, 1=>2 'foo'=>'biz' 都不会影响mylist

深拷贝通常是地柜的(如果循环引用会有问题),而且不是所有的对象都是可以深拷贝的。

3.11.6 构造函数 vs 初始化程序

虽然Python是OOP的语言,但是没有显式的构造函数的概念,没有 new 关键字,没有
真的实例化你的类。

python会为你创建实例并调用初始化程序,在你的对象创造出来之后,Python将它返回给你之前调用的第一个方法__init__

>>> from time import ctime
>>> class MyClass(object):
...     def __init__(self, date):
...         print "instance created at: ", date
...
>>> m = MyClass(ctime())
instance created at:  Sat Sep 27 10:31:22 2014
>>>

另外python也没有析构函数来销毁对象,只要让它离开作用域就行了,会被自动垃圾回收。

不过,你可以在 python对象中定义一个 __del__方法来当做析构函数,可以用 del my_object来显式的销毁对象。

动态实例属性

Python的另一个重要的特性是动态实例属性 dynamic instance attribute

实例的属性可以动态分配,
即使在类定义已经完成甚至是已经创建里实例,还是可以给你的实例添加一个之前没有提到过的属性

3.12. 代码风格

  • 四格对齐
  • 使用空格而非Tab
  • 不要像标题一样把一组代码放在同一行里
  • 创建文档字符串(docstring)

    C:\Documents and Settings\ganiks.liu\Desktop>vim foo.py
    """foo.py --sample module demostrating documentation strings """
    class Foo(object):
    """ Foo() - empty class ... tobe developed """
    def bar(x):
    """bar(x) - function docstring for bart, prints out its arg 'x'"""
    print x
    C:\Documents and Settings\ganiks.liu\Desktop>python
    Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import foo
    >>> foo.__doc__
    'foo.py --sample module demostrating documentation strings '
    >>> foo.Foo.__doc__
    ' Foo() - empty class ... tobe developed '
    >>> foo.bar.__doc__
    "bar(x) - function docstring for bart, prints out its arg 'x'"
    >>> help(foo)
    Help on module foo:
    NAME
    foo - foo.py --sample module demostrating documentation strings
    FILE
    c:\documents and settings\ganiks.liu\desktop\foo.py
    CLASSES
    __builtin__.object
        Foo
    class Foo(__builtin__.object)
     |  Foo() - empty class ... tobe developed
     |
     |  Data descriptors defined here:
     |
     |  __dict__
     |      dictionary for instance variables (if defined)
     |
     |  __weakref__
     |      list of weak references to the object (if defined)
    FUNCTIONS
    bar(x)
        bar(x) - function docstring for bart, prints out its arg 'x'

3.13. 总结

你可能感兴趣的:(跟我一起Django - 03 Django工程师一定要知道的Python)