Python 基础参考

目录

1 入门知识

2 数据结构

2.1 序列

2.1.1 序列包括列表、字符串 、元组

2.1.2 基本操作

numbers = [1,2,3,4,5,6,7,8,9,10] #声明

numbers[3:6] #Slice 分片操作
#=> [4,5,6] 第二个索引号的元素不包含

numbers[-3:]
#=> [8,9,10]

numbers[0:10:2]
 #=> [1,3,5,7,9]

numbers[10:0:-2]
#=> [10,8,6,4,2]

[1,2,3] + [4,5,6]
#=> [1,2,3,4,5,6]

[42] *6
#=> [42,42,42,42,42,42]

2.2 列表

helloList = list('Hello')  #字符串转换成字符列表
#=>['H','e','l','l','o']
''.join(helloList)  #将字符列表转换成字符串
#=>'Hello'

2.2.1 列表可以修改

#delete element
numbers[5] = 64
del numbers[5]
#分片插入元素
numbers[5:5] = [6,6]

#分片删除元素
numbers[2:8] = []
name = list('perl')
name[2:] = list('ar')
# name => ['p', 'e', 'a', 'r']

2.2.2 列表方法

  • append
    lst= [1,2,3]
    lst.append(4)
    lst
    #=> [1,2,3,4]
    
  • count

    某个元素出现的次数

    [1,5,6,7,53,1,5].count(1)
    #=> 2
    
  • extend

    比 lst1=lst1+lst2 效率高

    lst1.extend(lst2)
    
  • index
    ['who','where'].index('who')
    #=> 0
    
  • insert
    [1,2,3,4,5].insert(2, 'e')
    #=> [1,2,'e',3,4,5]
    
  • pop

    移除列表中的一个元素(默认最后一个),并返回该元素的值

    x=[1,2,3]
    x.pop()
    #=> 3
    x
    #=> [1,2]
    

    注:使用 pop 可以实现数据结构——栈

  • remove

    移除第一个匹配项

    [1,2,3,4,5,6,7,8].remove(5)
    #=> [1,2,3,4,6,7,8]
    
  • reverse
  • sort
    x.sort()#x 顺序已改变
    y = sorted(x)#y 为 x 排序后的列表
    

    sort 可选参数(也适用于 sorted):
    一、比较函数(见下一节)
    二、key
    x.sort(key=len)
    三、reverse
    x.sort(reverse=True)

  • 自定义排序(比较函数)

    自定义 compare(x, y)
    x<y 时返回负数,x>y 时返回正数,x=y 时返回 0
    内建 cmp 函数例子:

    cmp(42, 32)
    #=> 1
    cmp(99, 100)
    #=> -1
    cmp(5,5)
    #=> 0
    numbers.sort(cmp)
    

2.2.3 空列表 []

2.2.4 空元素列表 [None]

2.3 元组

元组不能被修改

2.3.1 声明

1,2,3 or (1,2,3)
#=> (1,2,3)
() #空元组
#=> ()
(1,) or 1,
#=> (1,)
3*(40+2,)
#=>(42,42,42)

2.3.2 tuple 函数

传入序列,返回元组

tuple([1,2,3])
#=> (1,2,3)

2.3.3 使用元组的场合

  • 元组可以在映射中当作键使用,列表不行(详见第四章)
  • 元组是很多内建方法的返回值。必须处理他们。

2.4 字符串

2.4.1 基本操作

所有标准的序列操作对字符串同样适用,唯一需要记住,字符串是不可变的。

2.4.2 格式化操作符

format = "Hello, %s. %s enough for ya?"
values = ('world', 'Hot')
print format % values
#=>Hello, world. Hot enough for ya?

2.4.3 常用方法

  • list, tuple

    list 将字符串转为列表,tuple 转为元组

  • find
    x.find("abc")
    #=>返回 index
    
  • join1
    seq = ['1','2','3']
    sep = '+'
    sep.join(seq)
    #=> '1+2+3'
    
  • lower,upper,title

    title 功能: 单词首字母大写

  • replace
    x.replace('123', '456')
    
  • split 是 join 的逆方法
    '/usr/bin/env'.split('/')
    #=>['','usr','bin','env']
    

    split 不提供参数时,默认根据空白字符分割,包括 tab、换行符等。

  • strip

    去除两侧空格,也可传参数,去除指定的头尾字符

  • translate

    同 replace 区别在于只处理单个字符。同时进行多个替换。
    有时比 replace 效率高的多。
    转换之前,需要完成一张转换表。可用 string 中的 maketrans

    maketrans('cs', 'kz')
    

2.4.4 原始字符串(raw string)

使字符串中的转义符无效
r'c:\newtest.txt'
\n 不会被解释为换行符

2.5 字典

当索引不好用时使用

2.5.1 创建

phonebook={'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}#直接创建
phonebook['Cecil']
#=> '3258'

phonebook={}
phonebook['Denver'] = '5345'
phonebook
#=> { 'Denver': '5345' }

2.5.2 使用 dict 函数初始化

items = [('name', 'Gumby'), ('age', 42)]
d = dict(items)
d
#=> {'age': 42, 'name': 'Gumby'}

d=dict(name='Gumby', age=42) #另一种声明

2.5.3 基本字典操作

len(d)
del d[k]
k in d#(检查是否存在 k 键)

自动添加,对不存在的键复制等于创建了一个新项

2.5.4 字典的字符串格式化

字符串 % 字典名

"Cecil's phone number is %(Cecil)s." % phonebook
#=>"Cecil's phone number is 3258."

2.5.5 字典方法

  • clear
    x={}
    y=x
    x['key'] = 'value'
    y
    #=> {'key': 'value'}
    

    一、x = {} =>y 没变
    二、x.clear() => y {}

  • copy & deepcopy
    x = {'key1': 'value1', 'key2': 'value2'}
    y = x.copy()
    

    一、y.remove('key2')
    y 和 x 均受影响

    二、y['key1'] = 'value3'
    y 受影响,x 不受

    deepcopy 避免了该问题,无论 y 怎么处理,x 都不会变。

  • fromkeys

    使用给定的键建立字典,值默认都为 None

    dict.fromkeys(['name', 'age'])
    

    也可自己提供值的默认值

    dict.fromkeys(['name', 'age'], 'unknown')
    
  • get

    更宽松的访问字典项方法

    d={}
    d['name']
    #=> 出错
    d.get('name')
    #=> None
    
  • has_key
    has_key 相当于 k in d。python3.0 不包含该方法。
    
  • items & iteritems

    字典项以列表形式返回

    d = {'key1': 'value1', 'key2': 'value2'}
    d.items()
    #=> [('key1', 'value1'), ('key2', 'value2')]
    it = d.iteritems()
    list(it)
    #=> [('key1', 'value1'), ('key2', 'value2')]
    
  • keys & iterkeys

    键以列表形式返回

  • values & itervalues

    值以列表形式返回

  • pop

    pop 用来获得对应于给定键的值,然后删除该项。

    d.pop('key1')
    
  • popitem

    类似于 list.pop
    因为字典没有顺序的概念,pop 出一个随机项。
    用处:若想一个接一个地处理并移除项,用该方法。

  • setdefault
    d.setdefault('name', 'N/A')
    #字典中无该键返回如下:
    #=> 'N/A'
    #字典中有{'name': 'Gumby'}
    #=> 'Gumby'
    
  • update

    利用一个字典项更新另外一个字典

    d.update(x) #更新源 d
    

2.6 其他

2.6.1 set

set 位于 sets 模块中。python2.3 以前需用 import sets。

set([0,1,2,3,0,1,2,3,4,5])#set 是由序列(或其他可迭代对象)构建的。
#=>set([0,1,2,3,4,5])#相同的成员会被忽略。
set(['fee', 'fie', 'foe'])
#=>set(['foe', 'fee', 'fie'])#与字典一样,顺序是未知的
  • 集合操作
    a = set([1,2,3])
    b = set([2,3,4])
    a | b
    #=>set([1,2,3,4])
    c = a & b
    #=>set([2,3])
    c.issubset(a)
    #=>True
    c >= a
    #=>False
    a.intersection(b)
    #=>set([2,3])
    a.difference(b)
    #=>set([1])
    a-b
    #=>set([1])
    a.symmetric_difference(b)
    #=>set([1,4])
    a.copy() is a
    #=>False
    

2.6.2 heap

优先序列的一种,以任意顺序追加元素。使用 pop 方法获取最小元素。
效率要比列表的 min 方法高。
堆算法:i 位置处的元素总是比 2*i 以及 2*i+1 位置处的元素小。

heappush(heap, x) 将 x 加入堆
heappop(heap) 将堆中最小元素返回
heapify(list) 将列表转为堆
heapreplace(heap, x) pop 出最小元素,加入 x
nlargest(n, iter) 返回 iter 中第 n 大的元素
nsmallest(n, iter) 返回 iter 中第 n 小的元素
from heapq import *
from random import shuffle
data=range(10)
shuffle(data)
heapify(data)
heappush(data, 0.4)
heap
#=>[0, 0.4, 4, 5, 1, 8, 9, 6, 7, 3, 2]
heappop(data)
#=>0

heapq 中的 nlargest 和 nsmallest 使用堆算法从可迭代对象中获取最大最小值

data=range(10)
shuffle(data)
nlargest(1, data)
#=>9
nsmallest(1,data)
#=>0

2.6.3 deque

Double-ended queue 双端队列

from collections import deque
q = deque(range(5))
q.append(5)#默认在队列右端追加
q.appendleft(6)
q
#=>deque([6,0,1,2,3,4,5])
q.pop()#默认从队伍右端 pop 出元素
#=>5
q.popleft()
#=>6
q.rotate(3)#右移三个元素
#=>deque([2,3,4,0,1])
q.rotate(-1)#左移一个元素
#=>deque([3,4,0,1,2])

3 语句

3.1 赋值语句

3.1.1 多个变量赋值

x, y, z = 1, 2, 3

3.1.2 交换变量

x, y = y, x

3.1.3 序列解包(sequence unpacking)

values = 1, 2, 3
values
#=> (1, 2, 3)
x, y, z = values
x
#=> 1

常用于函数方法返回元组时

key, value = scoundrel.popitem()

python 3.0 特殊解包

a, b, rest* = [1, 2, 3, 4]
rest
#=> [3, 4]

3.1.4 链式赋值

x = y = somefunction()
#等同于
y = somefunction()
x = y
#不一定等价于
x = somefunction()
y = somefunction()

3.1.5 增量赋值

与其他语言差不多+=、*=等

3.2 if 语句

如下会被视为 False:
False None 0 "" () [] {}
其他非空值被视为 True

True == 1 #=> True
False == 0 #=> True
#+end_src**** bool 函数
 #+begin_src python
 bool('test')
 #=> True
 bool(42)
 #=> True
 bool('')
 #=> False
  • if 语句写法
    if true:
    .....
    if 条件 1 and 条件 2:
    and, or, not
    
  • 更多比较运算符
    x is y  #x、y 是同一个对象
    x is not y
    x in y
    x not in y
    

    注:0 < age < 100 的形式也是可以的

    #字符串可以根据字母顺序进行比较
    "a"<"b" => True
    [1,2] < [2,1] #比较第一个值
    [2, [1, 4]] < [2, [1, 5]]
    

3.3 断言语句

age = -1
assert 0<age<100, 'message'
#=>breakdown

3.4 while 语句

x=1
while x<=100:
    print x
    x += 1

3.5 for 语句

for number in range(1, 100):
    print number

range 包含下限 1,不包含上限 1002

#循环遍历字典
for key in d:
    print key, d[key]

3.6 拓展

3.6.1 列表推导式(轻量级循环)

[x*x for x in range(10)]
#=> [0, 1, 4, 9, 25, 36, 49, 64, 81]

#可加条件:
[x*x for x in range(10) if x % 3 ==0]
#=>[0, 9, 36, 81]

[(x, y) for x in range(3) for y in range(3)]
#=>[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

#等同于
result = []
for x in range(3):
    for y in range(3):
result.append((x, y))

3.6.2 序列合并迭代(zip)

合并两个序列为一个元组列表

names = ['name1', 'name2']
ages = [12, 45]
zip(names, ages)
#=> [('name1', 12), ('name2', 45)]

#解包元组,遍历
for name, age in zip(names, ages):
    print name, 'is', age, 'years old'

3.6.3 编号迭代(enumerate)

enumerate 函数可以在提供索引功能的序列中,迭代返回键值对。

for index, string in enumerate(strings):
    if 'xxx' in string:
    strings[index] = '[censored]'

3.6.4 翻转和排序迭代(reversed, sorted)

reversed, sorted 与序列方法 reverse, sort 差不多,区别在于 reverse 和 sort 改变原调用对象
sorted 函数返回列表
reversed 函数返回一个可迭代对象

list(reversed('Hello,World!'))
#=>'!dlroW,olleH'

3.6.5 for…else 语句

for n in range(99, 81, -1):
    root = sqrt(n)
    if root == int(root):
	print n
	break
else:
    print "Didn't find it!"

break 直接进入 else 语句块

4 函数

4.1 定义函数

def fibs(num):
    'generate fib sequence'
    result = [0, 1]
    for i in range(num-2):
	result.append(result[-2] + result[-1])
    return result

4.2 参数

4.2.1 基本

函数内改变参数变量的值,不会改变外部传入变量的值。即值传递方式。
可变的数据结构,如列表。即引用传递方式。

4.2.2 不可变的数据结构,改变传入值的方法

  • 使用返回值

    def inc(x): return x+1
    
  • 包在列表中传递

    def inc([x]): x[0] = x[0] + 1
    foo = [10]
    inc(foo)
    foo
    #=> 11
    

4.2.3 参数过多,顺序难记

def hello(greeting, name):
    print '%s, %s' % (greeting, name)

hello('Hello', 'world')#参数顺序难记时,使用 参数名=传入值 的形式
hello(greeting='Hello', name='world')

4.2.4 参数默认值

def hello2(greeting='Hello', name='world'): #提供默认值
    print '%s, %s!' % (greeting, name)
hello2()
#=>Hello, world!

4.2.5 任意数量的参数

  • 参数定义时加上'*'
    def print_params(title, *params):
        print title, params#传入的 params 参数会作为元组使用
    print_params('Print params', 'param1', 'param2')
    #=>Print params
    #=>Print params ('param1', 'param2')
    
    print_params('Nothing')
    #=>Nothing ()
    
  • 参数定义时加上'**'
    def print_params2(**params):
        print params#传入的 params 参数会作为字典使用
    print_params2(x=1, y=2, z=3)
    #=>( 'x' : 1, 'y' : 2, 'z' : 3)
    

4.2.6 将元组和字典作为参数(以上逆过程)

#序列用法
def  add(x, y): return x + y
params = (1, 2)
add(*params)#加上*
#=>3

#字典用法
def hello3(greeting='Hello', name='world'):
    print '%s, %s!' % (greeting, name)
params = {'name': 'Sir Robin', 'greeting': 'Well met'}
hello3(**params)#加上**
#=>Well met, Sir Robin!

4.2.7 变量作用域

scope = vars()#vars()返回全局变量的字典
x=1
scope['x']
#=>1
scope['x'] += 1
x
#=>2

以上代码说明:
赋值语句实际上是在一个全局变量字典中建立了一个键值对,
只不过这个字典是对外隐藏的。这个“不可见”字典就是变量的作用域。
能够创建变量作用域的代码段有 def、class、lamda。

  • 函数内访问全局变量

    在函数作用域内调用同名全局变量,需使用 globals()['globalName']

    def combine(param):
        print param + globals()['param']
    param = 'berry'
    combine('Shrub')
    #=>Shrubberry
    
  • 函数内改变全局变量
    x=1
    def change_global():
        global x
        x = x + 1
    change_global()
    x
    #=>2
    

4.2.8 递归

def power(x, n):
    if n == 0:
    return 1
    else:
    return x * power(x, n-1)

4.3 查看函数说明

fibs.__doc__ 可访问函数说明、help(fibs) 更好用

4.4 空返回值

函数如果无返回值,返回 None

4.5 闭包

4.5.1 快速预览

以下是一段简单的闭包代码示例:

def foo():
    m=3
    n=5
    def bar():
	a=4
	return m+n+a
    return bar

>>>bar =  foo()
>>>bar()
12

说明:
bar 在 foo 函数的代码块中定义。我们称 bar 是 foo 的内部函数。

在 bar 的局部作用域中可以直接访问 foo 局部作用域中定义的 m、n 变量。
简单的说,这种内部函数可以使用外部函数变量的行为,就叫闭包。

那么闭包内部是如何来实现的呢?
我们一步步来,先看两个 python 内置的 object: <code>和<cell>

4.5.2 code object

code object 是 python 代码经过编译后的对象。
它用来存储一些与代码有关的信息以及 bytecode。

以下代码示例,演示了如何通过编译产生 code object
以及使用 exec 运行该代码,和使用 dis 方便地查看字节码。

code object 还有很多的特性可以访问。详细请看官方文档。

import dis
code_obj = compile('sum([1,2,3])',  '', 'single')

>>>exec(code_obj)
6

>>> dis.dis(code_obj)
  1           0 LOAD_NAME                0 (sum)
	      3 LOAD_CONST               0 (1)
	      6 LOAD_CONST               1 (2)
	      9 LOAD_CONST               2 (3)
	     12 BUILD_LIST                   3
	     15 CALL_FUNCTION          1
	     18 PRINT_EXPR
	     19 LOAD_CONST               3 (None)
	     22 RETURN_VALUE

那么,这跟我们的例子有什么关系?

>>> foo.func_code
<code object foo at 01FE92F0, file "<pyshell#50>", line 1>

我们可以看到,函数定义好之后,就可以通过[函数名.func_code]
访问该函数的 code object,之后我们会用到它的一些特性。

4.5.3 cell object

cell 对象的引入,是为了实现被多个作用域引用的变量。
对每一个这样的变量,都用一个 cell 对象来保存 其值

拿之前的示例来说,m 和 n 既在 foo 函数的作用域中被引用,又在 bar
函数的作用域中被引用,所以 m, n 引用的值,都会在一个 cell 对象中。

可以通过内部函数的__closure__或者 func_closure 特性查看 cell 对象:

>>> bar = foo()
>>> bar.__closure__
(<cell at 0x01FE8DF0: int object at 0x0186D888>, <cell at 0x01F694B0: int object at 0x0186D870>)

这两个 int 型的 cell 分别存储了 m 和 n 的值。
无论是在外部函数中定义,还是在内部函数中调用,引用的指向都是 cell 对象中的值。

注:内部函数无法修改 cell 对象中的值,如果尝试修改 m 的值,编译器会认为 m 是函数
bar 的局部变量,同时 foo 代码块中的 m 也会被认为是函数 foo 的局部变量,就会再把 m
认作闭包变量,两个 m 分别在各自的作用域下起作用。

4.5.4 闭包分析

  • 使用 dis3模块分析 foo 的 bytecode。
2          0 LOAD_CONST              1 (3)
	    3 STORE_DEREF               0 (m)

3          6 LOAD_CONST              2 (5)
	    9 STORE_DEREF               1 (n)

4          12 LOAD_CLOSURE         0 (m)
	    15 LOAD_CLOSURE         1 (n)
	    18 BUILD_TUPLE              2
	    21 LOAD_CONST             3 (<code object bar at 018D9848, file "<pyshell#1>", line 4>)
	    24 MAKE_CLOSURE         0
	    27 STORE_FAST               0 (bar)

7          30 LOAD_FAST                 0 (bar)
	    33 RETURN_VALUE

进行逐行分析:

LOAD_CONST 1 (3)
将 foo.func_code.co_consts [1] 的值"3"push 进栈。

STORE_DEREF 0 (m)
从栈顶 Pop 出"3"包装成 cell 对象存入 cell 与自由变量的存储区的第 0 槽。
将 cell 对象的地址信息赋给变量 m(闭包变量名记录在 func_code.cellvars)。
func_code.cellvars 的内容为('m', 'n')

LOAD_CLOSURE 0 (m)
将变量 m 的值 push 进栈,类似如下信息:
<cell at 0x01D572B0: int object at 0x0180D6F8>

LOAD_CLOSURE 1 (n)
类似变量 m 的处理,不在累述。

当前栈区状态:

1 <cell at 0x01D572B0: int object at 0x0180D6F8>
2 <cell at 0x01D86510: int object at 0x0180D6E0>
3


BUILD_TUPLE 2
将栈顶的两项取出,创建元组,并将该元组 push 进栈。

LOAD_CONST 3
从 foo.func_code.co_consts [3] 取出,该项为内部函数 bar 的 code object 的地址,将其 push 进栈
<code object bar at 018D9848, file "<pyshell#1>", line 4>

栈区状态:

1 <code object bar at 018D9848, file "<pyshell#1>", line 4>
2 (<cell at 0x01D572B0: int object at 0x0180D6F8>, <cell at 0x01D86510: int object at 0x0180D6E0>)
3


MAKE_CLOSURE 0
创建一个函数对象,将位于栈顶的 code object(bar 函数的 code)地址信息赋
给该函数对象的 func_code 特性;
将栈顶第二项(包含 cell 对象地址的元组)赋给该函数对象的 func_closure 特性;
最后将该函数对象地址信息 push 进栈。

STORE_FAST 0 (bar)
从栈顶取出之前创建的函数对象的地址信息赋给局部变量 bar(局部变量名记录在 func_code.co_varnames 中)
func_code.co_varnames 的内容为('bar',)
将变量 bar(记录在 func_code.cellvars [0] )绑定栈顶的函数对象地址。
LOAD_FAST 0 (bar)
将变量 bar 的值 push 进栈。

RETURN_VALUE
返回栈顶项,print bar 可以看到<function bar at 0x01D899F0>

  • 再分析 bar 函数就简单了
5           0 LOAD_CONST            1 (4)
	     3 STORE_FAST               0 (a)

6           6 LOAD_DEREF               0 (m)
	     9 LOAD_DEREF               1 (n)
	     12 BINARY_ADD
	     13 LOAD_FAST               0 (a)
	     16 BINARY_ADD
	     17 RETURN_VALUE

重点是 LOAD_DEREF,该方法主要是将 cell 对象中的 object 内容 push 进栈。大致过程如下:

根据变量 m 的值找到包装在 cell 内的 int object 的地址信息
m 的值:<cell at 0x01D572B0: int object at 0x0180D6F8>

根据地址取出 int 值,push 进栈。

4.6 函数柯里化

4.6.1 柯里化(Currying)

一个函数有多个参数,我们希望能固定其中几个参数的值。

from functools import partial
def foo(a,b,c):
    return a+b+c

foo2 = partial(foo, b=2)
foo2(a=1, c=3)
#=>6

看上去这跟提供参数默认值的情况类似。但默认值只能固定
为单个值,而柯里化能通过泛化出很多个函数,每个函数用
不同的固定值,来看一下 应用场景

from functools import partial
bin2dec = partial(int, base=2)
hex2dec = partial(int, base=16)

原 int 方法的定义为:int( x[, base]),base 参数的默认为 10
经过柯里化之后,可以用如下方式调用:

int('15') #=>15  using default base 10
bin2dec('01011') #=>11
hex2dec('67') #=>103

4.6.2 反柯里化(Uncurrying)

顾名思义,柯里化的逆过程。
将多个只含单个参数的函数模拟成一个多参数函数。

def foo(a):
    def bar(b):
	return a+b
    return bar

你可以像这样调用:foo(1)(4)或(foo(1))(4),都能得到正确的结果 5。

  • 那么反柯里化有什么用?

一些函数式编程语言,就是利用反柯里化来实现多参数函数。

4.7 拓展

4.7.1 斐波那契数列

fibs = [0, 1]
for i in range(8):
    fibs.append(fibs[-2] + fibs[-1])
fibs
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
#抽象成
fibs(num)

4.7.2 map/reduce/filter

  • map

    map(func, seq1, seq2, …])

    map(lambda x,y: x+y, [1,2,3,4,5], [6,7,8,9,10])#seq1 对应 arg1, seq2 对应 arg2
    #=>[7, 9, 11, 13, 15]
    
  • reduce
    reduce(func, seq)
    等同于 func(func(func(seq[0], seq[1]), seq[2])...)
    eg:
    reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
    #=>15
    
  • filter

    filter(func, seq)
    func 需返回布尔值

    filter(lambda x: x>3 and True or False, [1,2,3,4,5])
    #=>[4,5]
    filter(lambda x: True if x>3 else False, [1,2,3,4,5])
    #=>[4,5]
    

5

5.1 定义类

class Person:
    def setName(self, name):
	self.name = name

    def getName(self):
	return self.name

    def greet(self):
	print "Hello, world! I'm %s" % self.name

5.1.1 self 参数

class A:
    def a(self):
	print 'Hello'

i = A()
i.a
#=><bound method A.a of <__main__.A instance at 0x01E26648>>
i.a()
#=>Hello
#等同于
A.a(i)
#=>Hello

5.1.2 函数的变量绑定

class A:
    def a(self):
	print 'Hello'
def b():
    print 'World'

i=A()
i.a=b#i.a 是一个特性,可以绑定任何方法
i.a
#=><function b at 0x01E279B0>
i.a()
#=>World
A.a(i)
#=>Hello

i2=A()
c=i2.a#c 是方法变量
c
#=><bound method A.b of <__main__.A instance at 0x01E2F8A0>>
c()
#=>Hello

5.1.3 私有成员

python 不直接支持私有成员。
需要程序员自己把握从外部调用实例成员的时机
小技巧:加上双下划线__,使方法变为对外不可见

class Secretive:
    def __inaccessible(self):
	print "You can't find me"
    def accessible(self):
	print "You can find me"
	self.__inaccessible()
i=Secretive()
i.__inaccessible()
#=>Error
i.accessible()
#=>You can find me
#=>You can't find me
  • 内幕

    所有的以双下划线开头的成员方法"__方法名",都被转成"_类名__方法名"

    Secretive._Secretive__inaccessible
    #=><unbound method Secretive.__inaccessible>
    i._Secretive__inaccessible()
    #=>You can't find me
    
  • 使用单下划线

    这更像是一种习惯或者约定,程序员看到'_'开头的方法时,就不应该再从外部访问该方法。
    在使用 import 导入的时候,这些成员也不会被列出。

    def _privateMethod():
        print "You should not invoke this from outside"
    

5.2 类成员变量(非实例成员)

定义一个 Class 时,类空间域中的代码都会执行。

class C:
    print "Class C"#会被执行
#=>Class C
class A:
    count = 0#定义了一个类成员 number 并初始化为 0,有点类似于其他语言的静态成员
    def init(self):
	A.count += 1

m1=A()
m1.init()
A.count
#=>1

m2=A()
m2.init()
A.count
#=>2

#实例作用域同样适用
m1.count
#=>2
m2.count
#=>2

#实例作用域已分开
m1.count = 5#创建了 m1 的成员变量 count,m1.count 不再绑定类变量 A.count
m1.count
#=>5
m2.count
#=>2
m1.__dict__
#=>{'count': 5}

5.3 类静态方法和类方法

class StaticMethodTest:
    def staticMeth():
	print 'calling static method'
    staticMeth=staticmethod(staticMeth)
    def classMeth(cls):
	print 'calling class method'
    classMeth=classmethod(classMeth)
    def instanceMeth(self):
	print 'calling instance method'

s = StaticMethodTest()
s.staticMeth()
#=>calling static method
s.classMeth()
#=>calling class method
s.instanceMeth()
#=>calling instance method
StaticMethodTest.staticMeth()
#=>calling static method
StaticMethodTest.classMeth()
#=>calling class method
StaticMethodTest.instanceMeth()
#=> unbounded method error
#decorators 新语法
__metaclass__=type
  class StaticMethodTest:
      @staticmethod
      def staticMeth():
	  print 'calling static method'

5.4 属性

5.4.1 适用新式类

__metaclass__=type
class Retangle:
    def __init__(self):
	self.width=0
	self.height=0
    def setSize(self, size):
	self.width, self.height=size
    def getSize(self):
	return self.width, self.height
    size = property(getSize, setSize)

r = Retangle()
r.width=10
r.height=5
r.size
#=>(10, 5)
r.size = 150, 100
r.width
#=>150

5.4.2 适用旧式类

  • __getattr__(self,name)
  • __setattr__(self,name,value)
  • __delattr__(self,name)
class Retangle:
    def __setattr__(self, name, value):
	if name =='size':
	    self.width, self.height = value
	else:
	    self.__dict__[name] = value
    def __getattr__(self,name):
	if name == 'size':
	    return self.width, self.height
	else:
	    raise AttributeError

注:只有特性不存在时,才会调用__getattr__。
本例中 self.size 特性不存在,__getattr__会被调用。
如果访问 width 特性,则不会调用__getattr__。

5.5 继承

5.5.1 定义子类

class Parent:
    def init(self):
	print "Init Parent done"

class ChildA(Parent):
    def init(self):#重写父类的 init 方法
	print "Init Child done"

class ChildB(Parent):
    pass

p = Parent()
p.init()
#=>Init Parent done
a = ChildA()
a.init()
#=>Init Chile done
b = ChildB()
b.init()
#=>Init Parent done

5.5.2 查询类继承关系

  • issubclass(sub, parent) 判断两个类是否为父子关系
issubclass(ChildA, Parent)
#=>True
  • sub.__bases__ 查找一个类的父类
ChildA
#=><class __main__.ChildB at 0x01E44EA0>
ChildA.__bases__#查找父类
#=>(<class __main__.Parent at 0x01E1F340>,)

5.5.3 查询对象的类别

  • isinstance(instance, class) 判断对象是否属于某类
isinstance(a, ChildA)
#=>True
isinstance(a, Parent)
#=>True
  • instance or instance.__class__ 查询对象属于哪个类
a
#=><__main__.ChildA instance at 0x01E181C0>
a.__class__
#=><class __main__.ChildA at 0x01E32030>

5.6 多重继承

经典类指的是没有继承任何基类直接定义的类。
新式类指的是基类为"object"的类。
- 特性搜寻算法(MRO)
[旧]深度优先,从左到右进行搜索。
[新]广度优先,从继承层次下层从左到右开始搜索。

MultipleInheritance.png 经典类算法:

gc=GC()
gc.foo() # GC==>C1==>P1
#=>called P1-foo()
gc.bar() # GC==>C1==>P1==>P2
#=>called P2-bar()
C2.bar(gc)#才会调用 C2 中的 bar
#=>called C2-bar()

问题在于 C2 在继承树上与 GC 更亲近,一般的思维习惯是调用 C2

新式类算法:

gc=GC()
gc.foo() # GC==>C1==>C2==>P1
#=>called P1-foo()
gc.bar() # GC==>C1==>C2
#=>called C2-bar()

新式类可使用__mro__特性查看 mro 顺序

GC.__mro__
(<class '__main__.GC'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)

5.7 接口和内省

Python 不需要单独定义接口或抽象类
Python 变量 a,无所谓它是什么类型。调用 a.func 时,
只要保证对象 a 中存在特性 func 就能正常调用。
这就是 Python 强大的内省机制(即在运行时,获取对象的全部信息)。

  • hasattr 检查特性是否存在
  class A:
      def printNote(self):
	  print "A"
a = A()
hasattr(a, 'printNote')
#=>True
hasattr(a,'print')
#=>False
  • getattr(obj, 'attr', None) 获取特性信息。第三个参数表示找不到该属性时,返回的值 。
#甚至可以检查是否可调用
getattr(a,'printNote',None)#3.0 中已不再使用, getattr 可以使用默认值,此处是 None
#=><bound method A.printNote of <__main__.A instance at 0x01DA4C88>>

setattr(a,'name','Mr.Gumby')#相对应的方法 Setattr
>>>a.name
'Mr.Gumby'

5.8 类的特殊特性总结

特性 说明
__dict__ 类名字空间的特性字典
__doc__ 类的文档说明字符串
__name__ 类的名称
__module__ 类的模块名
__bases__ 该类所有父类组成的元组

6 异常

6.1 抛出异常

使用 raise 语句抛出异常

raise Exception('Error occured')
#=>Traceback (most recent call last):
#=>  File "<pyshell#96>", line 1, in <module>
#=>    raise Exception('Error occured')
#=>Exception: Error occured

6.2 内建异常类

参考手册“Built-in Exceptions”一节。
内建异常都在 exceptions 模块

6.3 自定义异常类

只需创建类时,继承 Exception 基类即可。

6.4 捕捉异常

  • try/except 语句
import exceptions
try:
    print 5/0
except ZeroDivisionError:
    print "Error: modulo by zero"
except (TypeError, NameError), e:#多个异常,放入元组中捕捉,e 是异常实例
    print e

注:3.0 中改为 except(TypeError, NameError) as e

  • try/except/else 语句

当未捕捉到异常时,执行 else 语句块

  • try/except/finally

finally 语句块无论是否捕捉到异常都会被执行,多用于清理资源。

6.5 常用异常类列表

类名 说明
Excepition 异常基类
AttributeError 特性引用或赋值失败
IOError 文件 IO 异常
IndexError 使用序列中不存在的索引
KeyError 使用映射中不存在的键
NameError 找不到特性(变量)名
SyntaxError 代码语法错误
TypeError 方法传入参数类型错误
ValueError 方法传入不合适的值,类型正确
ZeroDivisionError 除数为 0

6.6 DB 相关异常列表

异常 基类 说明
StandardError   所有异常的基类
Warning StandardError 非致命错误时引发
Error StandardError 所有错误类型的基类
InterfaceError Error 接口错误
DatabaseError Error 与数据库相关的错误基类
DataError DatabaseError 与数据相关的问题,值超出范围
OperationalError DatabaseError 数据库内部操作错误
IntegrityError DatabaseError 关系完整性错误,如键检查失败
InternalError DatabaseError 数据库内部错误
ProgrammingError DatabaseError 用户编程错误,如未找到表
NotSupportError DatabaseError 请求不支持的特性,如回滚

7 高级特性和隐藏特性

大多数隐藏特性,只对新式类起作用。可将赋值语句__metaclass__=type 放在模块
的最开始,或者定义类时继承自 object 类,来定义新式类。3.0 起没有“旧式“类了。

7.1 __init__、__del__

分别对应构造和析构方法,构造函数是被使用的最多的隐藏方法。
析构函数由于其内存释放的不确定性,调用时机未知,一般不建议使用。

7.1.1 继承体系中的__init__

__init__和一般的方法一样,子类会重写父类的构造。
为了调用父类的构造,一般有两种方法:

  • 调用未绑定的父类构造方法

该方法是 3.0 以前的老方法,属于历史遗留问题。
使用 super 函数简单明了。

class Drived(Base):
    def __init__(self):
	Base.__init__(self)#调用基类构造
	self.var1=5#自身特性赋值
  • 使用 super 函数

只适用于新式类。

class Drived(Base):
    def __init__(self):
	super(Drived, self).__init__()

7.2 集合相关__len__、__getitem__等

需要自行实现的隐藏方法有:

  1. __len__(self)
  2. __getitem__(self,key)
  3. __setitem__(self,key,value)
  4. __delitem__(self,key)

注意点:

  • 键允许是负整数

完全自己实现,代价太大,而且不一定实现的很好。
一般的做法:继承 UserList, UserString, UserDict
最新版本的 python 中,可以继承内建类了。可以直接继承内建 list 类。

class CounterList(list):
    def __init__(self, *args):
	super(CounterList, self).__init__(*args)
	self.counter = 0
    def __getitem__(self, index):
	self.counter += 1
	return super(CounterList, self).__getitem__(index)

7.3 迭代器__iter__

迭代器需要实现 next 方法,如果实现了__iter__方法,说明其是可迭代的。
因为__iter__方法定义,需要返回一个迭代器。

class Fibs:
    def __init__(self):
	self.a = 0
	self.b = 1
    def next(self):
	self.a, self.b = self.b, self.a+self.b
	return self.a
    def __iter__(self):
	return self
fibs = Fibs()
for f in fibs:#in 关键字后面应跟有可迭代对象
    if f >100:
	print f
	break
#=>144
it=iter(fibs)#iter 方法返回迭代器
it.next()
#=>233
fibs.next()
#=>377

7.3.1 从迭代器得到序列

class TestIterator:
    def __init__(self):
	self.value=0
    def next(self):
	self.value += 1
	if self.value > 10: raise StopIteration #没有这行,list 会无限循环下去
	return self.value
    def __iter__(self):
	return self
ti = TestIterator()
list(ti)
#=> [1,2,3,4,5,6,7,8,9,10]

7.4 生成器 yield 语句

生成器为了帮助程序员写出非常优雅的代码而出现的。
编写任何程序时不用生成器也是可以的。
生成器是一个包含 yield 语句的函数,当函数被调用时,函数体中的代码不会被执行,
而返回一个生成器(类似于有 next()方法的迭代器)。每次调用 next()请求,会执行到
yield 语句并返回一个值。return 语句意味着生成器要停止执行。
多用于递归。

def simpleGen():
    yield 1
    yield 2
    return
    yield 3

myG=simpleGen()#myG 为生成器
myG.next()#next 调用到 yield 语句得到返回值,并且中止函数运行
#=>1
myG.next()#继续函数运行,知道下一个 yield 语句
#=>2
myG.next()#已无未执行的 yield 语句
#=>StopIteration exception

for 循环会自动调用 next()
for 循环捕捉到 StopIteration 异常时停止。

for each in simpleGen():
	print each
1
2
  • 通过生成器直接生成列表
list(simpleGen())
#=>[1,2]
  • 列表推导式和生成推导式
g = (x*x for x in range(2,5))#g 是一个生成器
g.next()
#=>4
[x*x for x in range(2,5)]
#=>[4,9,16]
  • list(生成器): 可以使生成器立即求值

7.4.1 send 方法

def repeater(value):
    while True:
	new = (yield value)
	if new is not None: value = new

r = repeater(42)
r.next()
#=>42
r.send("hello world")#yield 语句后的变量被赋值为'hello world'
#=>'hello world'
r.next()
#=>'hello world'

7.4.2 throw、close 方法

throw 用于在生成器内(yield)引发一个异常。
close 用于关闭生成器,关闭之后执行 next 会接收到 StopIteration 异常。

7.4.3 旧版的对应方法

def flatten(nested):
    result=[]
    try:
	for sublist in nested:
	    for element in flatten(sublist):
		result.append(element)#对应 yield element
    except TypeError:
	result.append(nested)
    return result

7.4.4 借助生成器解决八皇后问题

判断最后一行,指定列号位置放入皇后后是否与之前的皇后位产生冲突

#state[0]=3 表示第一行第四列
def conflict(state, nextX):
    'state 是已放置好的皇后的位置信息,nextX 为新增皇后的行位置(对于空 state 也能正常运作)'
    nextY = len(state)
    for i in range(0, nextY):
	if abs(nextX-state[i]) in (0, nextY-i):
	    return True
    return False

递归从第一行开始逐行放入皇后,并判断是否与之前摆放完成的皇后们产生冲突

def queens(n, state=()):
    for pos in range(n):
	if not conflict(state, pos):
	    if len(state) == n-1:#表示仅最后一个位置未放置皇后,递归的最终层
		yield (pos,)
	    else:
		for result in queens(n, state + (pos,)):#当最后一层 yield 出内容后,此处 result 才会有结果。
		    yield (pos,) + result

list(queens(4, (1,)))
#=>[(3,0,2)]
list(queens(4))
#=>[(1,3,0,2), (2,0,3,1)]

让输出变的漂亮一些

def prettyprint(state):
    def line(x, length):
	return '. '*x + 'X ' + '. '*(length-x-1)
    for x in state:
	print line(x, len(state))

import random
prettyprint(random.choice(list(queens(4))))
#=>
. X . .
. . . X
X . . .
. . X .

8 模块

8.1 导入自定义模块

  • 向 sys.path 中追加(不常用)
import sys
sys.path.append('/Home/python')
  • 或者直接将模块放置在 python 的查找目录
print(sys.path)#可查看当前 python 查询目录
pprint.pprint(sys.path)#pprint 提供更加智能的打印输出

推荐放置在 site-packages 目录下

  • 使用系统环境变量 PYTHONPATH

Windows 用户变量中加入 PYTHONPATH 项
Linux 下在.bashrc 中追加:

export PYTHONPATH=$PYTHONPATH:~/Home/python
  • 模块更新后,重载,使用 reload()方法

8.2 仅导入定义,不运行模块内程序

可在模块可执行代码前加上判断:

if __name__ =='__main__': test()

导入的模块的__name__值为模块名。

hello3.__name__
#=>'hello3'

8.3

包就是包含模块的文件夹,要将普通的文件夹变为 python 包,
需要在文件夹下新建一个__init__.py 的文件。
导入包中模块的方式如下:

import package
import package.module1
from package import module2

8.4 查看模块说明

首先,需要 import 对应模块,以内建 copy 模块为例

import copy

8.4.1 dir 查看

dir 列出模块中的类、函数和变量。

[n for n in dir(copy) if not n.startswith('_')]#此处用列表推导式过滤掉了系统用的__开头的函数
#=>['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']

8.4.2 __all__变量

copy.__all__
#=>['Error', 'copy', 'deepcopy']

__all__是在源代码 copy.py 中被设置成'Error', 'copy', 'deepcopy'
__all__用来定义 public interface

如果使用 from copy import *的话,只能直接使用__all__中的函数。
如要使用 PyStringMap,有两种方式:
1) copy.PyStringMap
2) from copy import PyStringMap 之后调用 PyStringMap 即可

编写自己的模块时,最好要指定__all__,如果未指定,import *会导入所有
非下划线开头的特性。

8.4.3 help 和__doc__

help 更详细一些,会有函数签名。

8.4.4 查看源代码

__file__变量存放着源代码的路径
一些模块没有__file__变量,可能是用 C 语言写的。

8.5 核心模块

8.5.1 sys

sys.argv 获取执行程序所输入的参数列表
sys.exit(0) 退出程序,返回程序运行正常的返回值 0

sys.path sys.platform sys.stdin/stdout/stderr

8.5.2 os

os.path 子模块,负责当前操作系统的路径操作。常用 os.path.split、os.path.join。

environ 返回系统环境变量字典
system(cmd) 新建一个子 shell,执行系统命令行
sep 路径中目录间分隔符
pathsep 各路径之间的分隔符
linesep 行分隔符

system(r'c:/"test app.exe"')需要将带空格的目录或文件名用引号包起来,系统才能正常识别
解决方案 startfile(path),例:os.startfile(r'c:/test app.exe')
另外,system 函数根据操作系统不同执行方式也不同:

  • windows 启动外部程序后,当前 python 程序仍会继续执行
  • linux 启动外部程序后,当前 python 程序中断,外部程序执行完后,继续执行。

8.5.3 fileinput

给 py 文件行末追加行号的例子:
#numberlines.py
import fileinput
for line in fileinput.input(inplace=True):
    line = line.rstrip()
    num = fileinput.lineno()
    print '%-50s # %2i' % (line, num)

对自身进行编辑:

python  numberlines.py numberlines.py

fileinput.input 用于遍历多个输入流中的行,返回可迭代的对象。
可以用 for 循环,迭代对列表中的每一行进行处理。

常用方法列表:

filename 返回当前文件名
lineno 累积总行数
filelineno 返回当前文件行数
isfirstline 检查是否是文件第一行
isstdin 当前文件为 sys.stdin 时返回 true
nextfile 关闭当前文件,移动到下一个文件
close 关闭序列,结束迭代

8.5.4 time

包含 9 个整数的元组表示一个时间。
(年, 月, 日, 时, 分, 秒, 周(0~6), 一年中的第几天(366), 夏令时(0,1,-1))
重要的函数列表:

asctime([tuple]) 将时间元组转换为字符串
strptime(string[, format] 将字符串解析成时间元组
localtime([secs]) 将 1970 年起(具体与平台有关)秒数转换为日期元组
mktime(tuple) 将时间元组转换为秒数
sleep(secs) 休眠 secs 秒
time() 当前时间(1970 年起的秒数,平台相关

8.5.5 random

random 生成的是伪随机数。要获取真正的随机性,需调用 os 模块的 urandom 函数
random 模块常用函数列表:

random() 返回[0,1)之间的随机实数 n
getrandbits(n) 以长整型的方式返回一个随机二进制数(位数为 n)
uniform(a, b) 返回[a,b)之间的随机实数 n
randrange([start,] stop [,step] ) 返回 range[start, stop)中的随机整数,step 必须为整数
choice(seq) 随机返回序列中的一个元素
shuffle(seq[, random]) 将序列进行随机排列
sample(seq, n) 从序列中选出 n 个随机元素,同时保证不会选出同一个元素

8.5.6 shelve

简单的文件数据库方案。

import shelve
s = shelve.open('D:\\test.dat')#创建简单 DB 文件
s['x'] = ['a', 'b', 'c']
#s['x'].append('d')无效

temp = s['x']
temp.append('d')
s['x'] = temp#必须给 s['x']特性重新赋值修改才能写入 DB 文件
s.close()

s = shelve.open('D:\\test.dat')
s
#=>{'x' : ['a', 'b', 'c', 'd']}

pickle 模块与 shelve 密切相关(自动序列化相关)。

8.5.7 re(regular expression)

重要函数列表:

compile(pattern[, flags]) 根据正则表达式字符串创建模式对象
search(pattern, string[, flags]) 在字符串中寻找与模式匹配的第一个子字符串
findall(pattern, string 列出匹配的所有子字符串
match(pattern, string[, flags]) 从给定字符串的开头开始匹配正则表达式
split(pattern, string[, maxsplits=0] 根据模式的匹配项分割字符串
sub(pat, repl, string[, count=0]) 将所有 pat 模式匹配的项用 repl 替换
escape(string) 将正则表达式特殊字符转义

complie 可以实现一次性创建模式对象,其余方法不必在单独创建模式对象
如 pat.search(string)、pat.match(string)

  • 组的运用

    r'www\.(.+)\.com$'
    组 0 表示整个字符串,组 1 表示(.+)的部分。
    关于组的重要函数:

    group([group1, …]) 获取给定子模式的匹配项
    start([group]) 返回给定组的匹配项的开始位置
    end([group]) 返回结束位置
    span([group]) 返回给定组的开始和结束位置
    m = match(r'www\.(.*)\..{3}', 'www.python.org')
    m.group(1)
    #=>'python'
    m.start(1)
    #=>4
    m.end(1)
    #=>10
    m.span(1)
    #=>(4, 10)
    
    • 运用组进行字符串替换(有用)
    考虑将'*something*'替换成'<em>something</em>'
    
    emphasis_pattern = r'\*([^\*]+)\*'
    re.sub(emphasis_pattern, r'<em>\1</em>', 'Hello, *world*!')#\1 代表组 1
    #=>'Hello, <em>world</em>!'
    
    • 陷阱
    emphasis_pattern = r'\*([.+])\*'
    re.sub(emphasis_pattern, r'<em>\1</em>', '*This* is a *test*')
    #=>'<em>This* is a *test</em>'
    
    #修改版正则表达式如下:
    emphasis_pattern = r'\*([.+?])\*'#组中加上一个? 表示尽可能少的匹配
    
  • Amazing Template System
    #template.py
    import fileinput, re
    field_pat = re.compile(r'\[(.+?)\]')
    scope = {}
    def replacement(match):
        code=match.group(1)
        try:
    	return str(eval(code, scope))
        except SyntaxError:
    	exec code in scope
    	return ''
    
    lines = []
    for line in fileinput.input():
        lines.append(line)
    text = ' '.join(lines)
    print field_pat.sub(replacement, text)
    
    #test.txt
    [x=2]
    [y=3]
    The sum of [x] and [y] is [x+y].
    
    python template.py test.txt
    #=>The sum of 2 and 3 is 5
    
    • execfile()可直接执行含 python 语法定义的文本文件
    • string 模块中的 Template 类提供了非常完美的模板系统
  • 一些提示
    • pattern 选项(规定出现次数)
    (pattern)? pattern 为可选项
    (pattern)* 允许重复 0 次或者多次
    (pattern)+ 允许重复 1 次或者多次
    (pattern){m,n} 允许重复 m~n 次
    • 字符串开头和结尾

    ^和$分别表示匹配开头和结尾。
    '^http'只匹配 http 开头的字符串

    • 关于正则表达式的可读性(VERBOSE flag)
    emphasis_pattern = re.compile(r'''
        \*    #Beginning emphasis tag -- an asterisk
        (      #Begin group for capturing phrase
        [^\*]+    #Capture anything except asterisks
        )      #End group
        \*    #Ending emphasis tag
        ''', re.VERBOSE)
    

    VERBOSE 标记会忽略空白字符、tab、注释等等

8.5.8 ConfigParser

配置文件:

[numbers]
pi: 3.1415926535897931
[messages]
greeting: Welcome

读取配置文件:

from ConfigParser import ConfigParser
CONFIGFILE = "testconfig.txt"
config = ConfigParser()
config.read(CONFIGFILE)
print config.get('messages', 'greeting')
print config.getfloat('numbers', 'pi') * (4**2)

8.5.9 Logging

import logging
logging.basicConfig(level=logging.INFO, filename='Mylog.log')
logging.info('Starting program')

8.6 其他常用模块

模块名 说明
cmath 复数运算
webbrowser 可指定 URL 打开网页浏览器
logging 记录日志用
itertools 迭代工具
bisect 实现了二元查找
functools 包含 map, filter, reduce
operator 内建的基本运算方法都在此模块
inspect 对象查看器
exceptions 异常处理
webbrowser 网络浏览器相关
difflib 序列间的比较
hashlib 根据字符串计算'签名',多用在加密和安全性
csv 处理 csv 文件
datetime time 不能满足要求时使用,接口更直观
cmd 用它编写命令行解释器,可以自定义命令

测试分析模块

timeit 衡量代码片段运行时间的工具,用于性能测试
profile 用于代码片段效率的全面分析
trace 判断哪些代码执行了,哪些未执行
dis 反汇编模块,查看对应的汇编代码

8.7 集成其他语言模块

语言 解决方案
C/C++ SWIG
.Net IronPython
Java JPython

8.8 可在 PyPI 上寻找 python 的最新的拓展包

9 文件

9.1 Open

open(name[, module[, buffering]]) 返回一个文件对象

  • name 文件名
  • module 文件模式(可选)
'r' 读模式
'w' 写模式
'a' 追加模式
'b' 二进制模式(可添加到其他模式中使用)
'+' 读写模式(可添加到其他模式中使用)

默认为读模式

  • buffering 缓冲(可选)
0 不使用内存缓冲,直接读写硬盘
1 或者任何负数 使用内存缓冲,并且使用默认缓冲区大小
大于 1 的数 缓冲区大小(单位:字节)

9.2 文件对象的基本方法

9.2.1 读和写

在一次 open-close 的区块内,使用 Write 会重写文件内容。

f = open('test.txt', 'w')
f.write('Append text.')#重写文件内容
f.write('Append text2.')#追加内容
f.close

读取操作 read,不带参数时读取全文,带数字参数时,
读取指定的字符数,并且记录当前位置,下次调用 read 时,
从该位置继续读取。

f = open('test.txt', 'r')#r 也可不加,默认是读取模式打开
f.read(4)
#=>'Appe'
f.read()
#=>'nd text.'
f.close()

9.2.2 Pipe

管道符号的意义:将前一个命令的 stdout 作为下一个命令的 stdin 执行

# somescript.py
import sys
text = sys.stdin.read()
words = text.split()
wordcount = len(words)
print 'Wordcount:', wordcount
# somefile.txt
abc def
ghi
cat somefile.txt | python somescript.py
#=>Wordcount:3

9.2.3 随机访问(seek 和 tell)

seek(offset[, whenrc]) 当前位置移动到 offset 处, whenrc:默认 0 从文件开头计算偏移,1 从前次的位置,2 从文件结尾
tell() 返回当前文件的位置(返回值类型:长整型)

9.2.4 按行读写

  • file.readline() 从当前位置开始直到一个换行符出现(换行符也会被读取)

可选非负整数作为参数,指定可以读取的最大字符数。
readlines 返回包含所有行的列表

  • file.writelines() 参数可以是字符串序列或者 None。没有 writeline 方法, 因为可以使用 write。

9.2.5 总是关闭文件

  • 可在 finally 关闭文件
  • Python 2.5 中引入的 with 语句
with open("somefile.txt") as somefile:
    do_something(somefile)
#with 语句块结束时,关闭 somefile,等同于 finally 效果

事实上,支持__enter__和__exit__方法的对象,都能使用 with 语句。

9.3 迭代处理读取内容

先定义一个处理字符串的方法,后面都会用到:

def process(string):
    print 'Processing: ', string

9.3.1 while 循环处理每个字符

f = open(filename)
while True:
    char = f.read(1)
    if not char: break
    process(char)
f.close()

9.3.2 while 循环处理每一行

f = open(filename)
while True:
    line = f.readline()
    if not line: break
    process(line)
f.close()

9.3.3 一次性读取所有内容

文件不大时使用,非常方便。

f = open(filename)
for char in f.read():
    process(char)
#or
for line in f.readlines():
    process(line)
f.close

9.3.4 fileinput

fileinput 是既方便又没有一次性读取的内存占用问题的解决方案。

import fileinput
for line in fileinput.input(filename):
    process(line)

9.3.5 2.2 版起可迭代的文件对象

f = open(filename)
for line in f:
    process(line)
f.close

可以更懒,只要 open 文件是只读模式, 关闭文件不是必须的!

for line in open(filename):
    process(line)

关闭文件交给 python 去完成了。

  • stdin 也是可迭代的类文件对象
  • list(open(filename)与 readlines 达到的效果一样
  • firstline, secondline = open(filename) 直接解包放入变量中

10 网络

10.1 socket

10.1.1 构造

socket 对象可选参数列表:

  1. 地址族 默认 socket.AF_INET
  2. 流 默认 socket.SOCK_STREAM(基于 TCP) 或者 数据报 socket.SOCK_DGRAM(基于 UDP)
  3. 使用的协议 默认 0

10.1.2 Server

  • bind 绑定地址,参数为(host, port)元组
  • listen 只有一个参数,指定允许排队等待的连接数目
  • accept

该方法会阻塞直到有客户端连接,而后返回一个(client, address)的元组。
当前 accept 处理完之后,会调用另一个 accept(),即队列方式。
注意:使用这种方式称为同步网络编程。

10.1.3 send, recv

  • send 参数传入字符串
  • recv 参数传入接收数据的最大字符数,1024 是个较好的选择。

10.1.4 简单示例

#socket server test
import socket
s = socket.socket()
host = socket.gethostname()
port =1234

s.bind((host, port))
s.listen(5)
while True:
    c, addr = s.accept()
    print 'Got connection from', addr
    c.send('Thank you for connectin')
    c.close()
#socket client test
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234

s.connect((host, port))
print s.recv(1024)
s.close()

命令行调用结果:

  • Server 端

    SocketServerResult.png

  • Client 端:

    SocketClientResult.png

10.2 urllib, urllib2

这两个模块几乎可以把任何 URL 所指向的东西用做程序的输入。
urllib2 更好一些,需要使用 http 验证、cookie 或者要为自己的协议
写扩展程序时,使用 urllib2。

  • urllib.urlopen(url) 返回一个类文件对象
  • urllib.urlretrieve(url, filename) 将 url 获取的内容保存成本地文件

注意:filename 默认 None,不指定的话,文件会临时存放在某位置,
用 open 函数可以打开它。清理临时文件,调用 urlcleanup 函数。

urllib 的其他一些功能:

quote(string[, safe]) 负责转移成 url 字符串
quote_plus(string[, safe]) 在 quote 的基础上,将空格转为'+'
unquote(string) 与 quote 相反
unquote_plus(string) 与 quote_plus 相反
urlencode(query[, doseq] 把映射或者包含两个元素的元组的序列转换成 URL 格式字符串

10.3 webservice

10.4 其他网络模块

asyncore 异步 socket 处理程序
asynchat asynchat 的加强版
cgi 基本的 CGI 支持
Cookie Cookie 对象操作,主要用于服务器
cookielib 客户端 cookie 支持
email E-mail 消息支持
ftplib FTP 客户端模块
httplib HTTP 客户端模块
imaplib IMAP4 客户端模块
mailbox 读取几种邮箱的格式
nntplib NNTP 客户端模块
poplib POP 客户端模块
robotparser 支持解析 Web 服务器的 robot 文件
SimpleXMLRPCServer 简单的 XML-RPC 服务器
smtpd SMTP 服务器模块
smtplib SMTP 客户端模块
telnetlib Telnet 客户端模块
urlparse 支持解析 URL
xmlrpclib XML-RPC 的客户端支持

11 测试

11.1 调试

11.1.1 命令行方式

启动调试器 python -m pdb test.py

11.1.2 IDLE 方式

__DEBUG = True
def test():
    a = 5
    if __DEBUG:
	import pdb
	pdb.set_trace()
    b = 4
    print a + b
pdb.run('mymodule.test()')
> <string>(1)<module>()
(Pdb) c
> d:\home\temp\mymodule.py(7)test()
-> b = 4
(Pdb) l
  2     def test():
  3         a = 5
  4         if __DEBUG:
  5             import pdb
  6             pdb.set_trace()
  7  ->     b = 4
  8         print a + b
  9
[EOF]

11.1.3 常用指令

命令 用途
break 或 b 设置断点
continue 或 c 继续执行程序
list 或 l 查看当前行的代码段
step 或 s 进入函数
return 或 r 执行代码直到从当前函数返回
exit 或 q 中止并退出
next 或 n 执行下一行
pp 打印变量的值
help 帮助

11.2 doctest

#mytest.py
def square(x):
    '''
    Square a number and returns the result.

    >>> square(2)
    4
    >>> square(3)
    8
    '''
    return x*x

if __name__=='__main__':
    import doctest, mytest
    doctest.testmod(mytest)

以上例子,doctest.testmod 会检查文档中'>>>',并执行例子进行检查。

$ python mytest.py

**********************************************************************
File "/home/chrischen/temp/mytest.py", line 7, in mytest.square
Failed example:
    square(3)
Expected:
    8
Got:
    9
**********************************************************************
1 items had failures:
   1 of   2 in mytest.square
***Test Failed*** 1 failures.

python mytest.py -v 可以获得更多输出。

11.3 unittest

假设要写一个模块,my_math,测试其中计算乘积的函数 product。<

import unittest, my_math
class ProductTestCase(unittest.TestCase):
    def testIntegers(self):
	for x in xrange(-10, 10):
	    for y in xrange(-10, 10):
		p=my_math.product(x, y)
		self.failUnless(p == x*y, 'Integer multiplication failed')

    def testFloats(self):
	for x in xrange(-10, 10):
	    for y in xrange(-10, 10):
		x = x/10.0
		y = y/10.0
		p = my_math.product(x, y)
		self.failUnless(p == x*y, 'Float multiplication failed')

    if __name__ == '__main__': unittest.main()

unittest.main()负责实例化所有 unittest.TestCase 的子类,运行所有名字以 test 开头的函数。
failUnless 检查结果给出错误提示。下表列出其他的检查方法:

assert_(expr[, msg]), failUnless 如果表达式为假则失败
failIf(expr[, msg]) 与 assert 相反
assertEqual(x, y[, msg]), failUnlessEqual 两个值不同则失败
assertNotEqual(x, y[,msg]), failIfEqual 两个值相同则失败
assertAlmostEqual(x,y[, places[,msg]]), failUnlessAlmostEqual 类似于 assertEqual,处理 float 的方式上不同
assertNotAlmostEqual(x,y[, places[,msg]]), failIfAlmostEqual 与 assertAlmostEqual 相反
assertRaises(exc, callable, …), failUnlessRaises 调用 callable,引发 exc 异常,则成功;否则失败
fail([msg]) 无条件失败

unittest 模块会区分由异常引发的错误和调用 failUnless 而导致的失败。

11.4 代码检查

11.4.1 PyLint

需先安装 astng 和 logilab-common
安装方式都是:python setup.py install
使用 pylint modulename:
python my_math
报告包含:Raw metrics、Statistics by type、Duplication、Messages by category、Messages、Global evaluation
PyLint 的更多功能

  • pyreverse: an UML diagram generator
  • symilar: an independent similarities checker
  • epylint: Emacs and Flymake compatible Pylint
  • pylint-gui: a graphical interface

还可以选择 PyChecker,更老牌的代码检查工具。
在 Python 中使用命令行工具的方式是使用 subprocess 模块。

11.5 性能测试

11.5.1 profile 模块(标准库)

import profile
from my_math import product
profile.run('product(1, 2)')

结果如下:

4 function calls in 0.024 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.024    0.024    0.024    0.024 :0(setprofile)
     1    0.000    0.000    0.000    0.000 <string>:1(<module>)
     1    0.000    0.000    0.000    0.000 my_math.py:1(product)
     1    0.000    0.000    0.024    0.024 profile:0(product(1, 2))
     0    0.000             0.000          profile:0(profiler)
  • 还可以以编程的方式分析结果
import pstats
p = pstats.Stats('my_math.profile')

对 p 对象进行分析即可。

  • 另一个选择 hotshot 模块

11.6 其他可选工具

py.test, nose, trace.py(覆盖率), PyUnit(GUI 单元测试)

12 发布程序

12.1 制作安装脚本

使用 distutils 模块编写 setup.py

from distutils.core import setup
setup(name='my_math',
      version='1.0',
      description='A simple example',
      author='ChrisChen',
      py_modules=['my_math'])
python setup.py install

setuptools 项目基于 distutils,但提供了更多的功能。
另外,目前为止,没有标准的 Uninstall 命令。需要手动卸载。

12.2 制作安装包

  • 使用命令制作压缩包:
python setup.py sdist
  • 关于 MANIFEST 文件

在创建发布程序的时候,程序同时自动创建 MANIFEST 文件,该文件
包含所有文件的列表。
在此之前可以提供 MANIFEST.in 文件作为输入,此文件时 MANIFEST 的模板。
可以用如下命令来指定需要包含的文件。

include somedir/somefile.txt
include somedir/*
  • MANIFEST 的更新

当需发布的文件目录发生改变时,需要手动删除由发布程序自动生成的 MANIFEST
文件,要不然发布程序会直接读取之前的 MANIFEST 文件,而不会重新生成。

  • Windows 发布
python setup.py bdist --format=wininst

直接产生一个 win32exe 文件。注意用此方式发布的安装包非常基础。

  • Linux RPM 发布
python setup.py bdist --format=rpm

12.3 py2exe

py2exe 直接将 python 运行环境打包,用户不必单独安装 python 解释器。
配合 Inno Setup 可完成“真正的安装程序”。

13 实用的内建函数

函数 说明
dir([obj]) 显示对象的属性,参数省略时,显示全局变量名
help([obj]) 显示对象的文档字符串
int(obj) 将一个对象转换为整形
len(obj) 返回对象的长度
open(fn, mode) 以相应的 mode 方式(r 读 w 写)打开一个文件名为 fn 的文件
range([start,] stop [,step] 返回整数列表,起始值 start,结束值 stop-1,步长 step
raw_input([str]) 等待用户输入一个字符串,str 为提示信息
str(obj) 将一个对象转换为字符串
type(obj) 返回对象的类型(返回值本身是一个 type 对象)

13.1 pass

pass 函数什么事情都不做 用作预留占位符
python 空代码块是非法的。需要如下做:

if x==0:
    pass

13.2 del

x=1
del x
x
#=>报错,del 不但删除内容,也删除变量名
x=1
y=x
del x
y
#=>1 原因:该情况 del 只删除了名称

13.3 exec 和 eval

  • exec

exec 执行字符串中的 python 语句
(风险极大,慎重,并且是很严重的安全漏洞)

exec "print 'Hello,world!'"

字符串代码,单独一个命名空间

scope={}
exec 'sqrt = 1' in scope
scope['sqrt']
#=>1
len(scope)
#=>2
scope.keys()
#=> ['sqrt', '__builtins__']
  • eval

eval 会计算 python 表达式(字符串形式书写),返回结果值
赋值语句不能使用 eval(抛出 SyntaxError),只能用 exec 执行

eval(raw_input('Enter an arithmetic expression: '))

eval 同样也可使用命名空间

scope = {}
scope['x'] = 2
scope['y'] = 3
eval('x * y', scope)
#=>6

14 ipython magicfunc

14.1 常用

Command Description
%quickref Display the IPython Quick Reference Card
%magic Display detailed documentation for all of the available magic commands
%debug Enter the interactive debugger at the bottom of the last exception traceback
%hist Print command input (and optionally output) history
%pdb Automatically enter debugger after any exception
%paste Execute pre-formatted Python code from clipboard
%cpaste Open a special prompt for manually pasting Python code to be executed
%reset Delete all variables / names defined in interactive namespace
%page OBJECT Pretty print the object and display it through a pager
%run script.py Run a Python script inside IPython
%prun statement Execute statement with cProfile and report the profiler output
%time statement Report the execution time of single statement
%timeit statement Run a statement multiple times to compute an emsemble average execution time.
%who, %who_ls, %whos Display variables defined in interactive namespace, with varying levels of information / verbosity
%xdel variable Delete a variable and attempt to clear any references to the object in the IPython internals

14.2 log

%logstart, %logoff, %logon, %logstate, %logstop

14.3 system-related

Command Description
!cmd Execute cmd in the system shell
output = !cmd args Run cmd and store the stdout in output
%alias alias_name cmd Define an alias for a system (shell) command
%bookmark Utilize IPython’s directory bookmarking system
%cd Change system working directory to passed directory
%pwd Return the current system working directory
%pushd directory Place current directory on stack and change to target directory
%popd Change to directory popped off the top of the stack
%dirs Return a list containing the current directory stack
%dhist Print the history of visited directories
%env Return the system environment variables as a dict

14.4 debuger

Command Action
h(elp) Display command list
help command Show documentation for command
c(ontinue) Resume program execution
q(uit) Exit debugger without executing any more code
b(reak) number Set breakpoint at number in current file
b path/to/file.py:number Set breakpoint at line number in specified file
s(tep) Step into function call
n(ext) Execute current line and advance to next line at current level
u(p) / d(own) Move up/down in function call stack
a(rgs) Show arguments for current function
debug statement Invoke statement statement in new (recursive) debugger
l(ist) statement Show current position and context at current level of stack
w(here) Print full stack trace with context at current position

14.5 %lprun

%lprun -f func1 -f func2 statement_to_profile

15 一些提示

15.0.1 关于变量作用域

python2.1 以前,支持三种作用域,分别为局部、全局、内置作用域
2.1 实现了对闭包的支持,引入了嵌套作用域。
变量的查找顺序为 LEGB(Local, Enclosing, Global, Built-in)

15.0.2 input 与 raw_input

input 只支持合法的 python 表达式
raw_input 所有输入以字符串形式作为原始数据 尽可能使用 raw_input

15.0.3 长字符串 跨越多行

使用三个单引号或者三个双引号
''' blablabla
blablabla…'''
注:三引号内的单引号和双引号,可以不用转义符号

15.0.4 普通字符串忽略代码中的换行符

换行处加上\

" 1 + 2 + \
4 + 5"
#=>"1 + 2 + 4 + 5"

15.0.5 关于表示路径的转义

r'C:\TestFolder\test.txt'

15.0.6 Unicode 字符串

u'Hello World!'

在 python 3.0 以上,所有字符串都为 unicode 字符串

15.0.7 使用逗号打印多个表达式

print 'Age', 42, '?'

15.0.8 ASCII 码转换

chr(n) & ord(c)

chr(number)
#=>对应的 char
ord(char)
#=>对应的 ASCII 码编号

15.0.9 检查特性是否为字符串

通过和字符串拼接来检查参数类型是否为字符串
以下代码检查参数如果是字符串,是则抛出异常。

try: var + ' '
except TypeError: pass
else: raise TypeError

15.0.10 python 虚拟环境

可使用 virtualenv,适用情况:

  1. 使用多个 python 版本进行开发,环境变量只能设一个。
  2. site-packages 在多人共享的开发机上,没有写入权限。

15.0.11 推荐工具包

ipython, pip, virtualenv, pylint(logilab_astng, logilab_common), py2exe, pyQt

  • pip 常用命令

pip install SomePackage==1.0
pip show –files SomePackage
pip list –outdated
pip install –upgrade SomePackage
pip uninstall SomePackage

脚注:

1

最常用的例子:'\\'.join(dirs)

2

range(1,100)实际是 1~99

3

函数经过编译的 bytecode,实际上放在 func.func_code.co_code 中,dis 模块对其做了解析,使其更容易阅读。