我的Python Cookbook

Table of Contents

1 开发工具

  • IPython

编辑器和IDE推荐:

  • Emacs
  • Vim
  • PyCharm
  • WingIde
  • Visual Studio

这里有份详细的对比:http://stackoverflow.com/questions/81584/what-ide-to-use-for-python

2 字符串

2.1 字符串对齐

让字符串“The end”左右边都将有20个“+”:

n [11]: print 'The end'.center(20,'+')
++++++The end+++++++

用直接输出字符串的方法很麻烦,也影响代码的外观。使用center方法比较容易一些。

2.2 字符串连接技巧

如下两个变量,hello和world:

hello = 'hello '
world = 'world'

如果将它们连接在一起,可以使用最先考虑到的字符串连接方式:

hello + world

但是,用”+“和的性能比较低下,用%s可以更加方便地处理好字符串的连接:

strs = '%s%s' % (hello, world)

这样做的好处:

1、可以很好控制浮点数的位数;

2、不要再对数字型用str转换了,因为%s已经自动完成该功能了

2.3 生成等宽数字

比如生成01 02 03这样格式的字符串:

for i in xrange(11):
    print '%02d' % i

或者:

for i in xrange(11):
    print '{0:02d}'.format(i)

2.4 字符串转raw_string

Python里没有现成的函数可以把字符串转成raw string,但我们经常遇到这种问题。可以用两种方法搞定:

1、repr

a = '\\test'
a = repr(a).[1:-1]

2、eval

a = '\\test'
a = eval('"%s"'%a)

eval的太麻烦,而且有安全隐患,推荐第一种方法。

3 字典

3.1 key和value逆转

利用字典解析将{"a": 1, "b": 2, "c":3}转换成{1: "a", 2: "b", 3: "c"}:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> {k:v for k,v in d.items()}
{'a': 1, 'c': 3, 'b': 2}

Dict的items方法返回一个列表,列表中每个元素都是一个包含key和value的元组:

>>> d.items()
[('a', 1), ('c', 3), ('b', 2)

3.2 按key排序

内置的sorted函数可以为cmp参数指定一个lambda表达式,告诉函数应该如何去做比较判断,由于dict对象的items函数可以返回一个列表,每一个元素都是key和value组成的元组,只用取元组第二个元素(value)做比较即可:

>>> d  = {"1": 'a', '2': 'b', '4': 'c', '3': 'd'}
>>> dict(sorted(d.items(), cmp=lambda x, y: cmp(x[1], y[1])))
{'1': 'a', '2': 'b', '3': 'd', '4': 'c'}

4 列表

4.1 列表解析

Python既支持简单的表达式,也有高级的表达式。Python列表解析可以根据列表中的表达式计算结果来创建一个新的列表。例如,我需要建立一个新的列表,列表元素包含了1~100之间的所有偶数,C语言中我们可以用这样的方法完成查找偶数:

#include <stdio.h>

int main()
{
    int num = 100;
    int i = 0;

    for ( ; i <= 100; i++)
    {
	if (i%2 == 0)
	printf("%d\n", i);
    }
    return 0;
}

同样,以这样的思维在Python中也可以这样写:

>>> for i in range(101):
... if i%2 == 0:
... print i

显然Python中要敲打的代码数量比C语言代码少多了,但是我们利用列表解析还可以更加省略,甚至只需要一行代码就完成了计算功能:

>>> l = [x for x in range(101) if x%2 == 0]                                         (1)
>>> l
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
>>>

注意(1)那行代码,l是定义的一个新的列表,在方括号中,首先是一个循环,range(101)会将1~100的数连续存储在l列表中,每一次循环,x都会赋上列表当前的索引值,然后再将这个值与2做取模运算,如果结果为0,则证明是一个偶数,接着将x赋值给方括号中开头的x。整个顺序我通过标号标记在表达式中:

l = [(3)x (1)for x in range(101) if  (2)x%2 == 0]

同样,稍加改造,可以计算1~100间所有奇数:

l = [x for x in range(101) if x%2 == 1]

4.2 清空列表

l = range(10)
print l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

清空l这个列表有三种方法:

  • 第一种方法:del l[:],这种方法最彻底,将从内存中删除。
  • 第二种方法:l = [],这种方法仅仅是把l指向到了一个新的、空的队列对象中。
  • 第三种方法:l[:] = [],这种本人觉得应该也是和第二种方法差不多。

为了节约内存的话,用第一种方法其实是不错的。

4.3 复制列表

将列表赋值给变量其实是将列表的引用(即内存地址)赋给了变量,如:

tmp = urls

就是将该列表引用的地址赋值给了tmp变量,所以无论如何改变urls,都会影响到tmp的变化。如果要想tmp复制一份不会随着urls变化而变化的列表,需要重新创建一个列表对象:

tmp = urls[:]

4.4 遍历列表时带上下标

for i, value in enumerate(["a", "b", "c"]):
    print(i, value)

4.5 平坦(flatten)嵌套列表

如,列表:

[[1, 2], [3, 4], [5]]

平坦为:

[1, 2, 3, 4, 5]

实现如下:

def flatten_list(lst):
    """
    >>> flatten_list([[1, 2], [3, 4], [5]])
    [1, 2, 3, 4, 5]
    """
    def do_flatten():
	for item in lst:
	    if hasattr(item, "__iter__") and isinstance(item, list):
		for sub_item in item:
		    yield sub_item
	    else:
		yield item

    return list(do_flatten())

4.6 统计列表各项重复次数

from collections import Counter

Counter([1, 2, 3, 4, 5, 5, 3, 1]) # => Counter({1: 2, 3: 2, 5: 2, 2: 1, 4: 1}

自己实现版:

from collections import defaultdict

count = defaultdict(int)

for i in [1, 2, 3, 4, 5, 5, 3, 1]:
    count[i] += 1

print(count) # => defaultdict(<type 'int'>, {1: 2, 2: 1, 3: 2, 4: 1, 5: 2})

5 生成器

5.1 生成器表达式

In [9]: l = range(10)

In [10]: it = (i for i in l if i%2 == 0)

In [11]: list(it)
Out[11]: [0, 2, 4, 6, 8]

生成器表达式(Generator Expressions)与列表解析不同,语法上是括号包围的,产生的结果是一个可迭代对象。

Generator expressions永远写到括号里面的,还可以:

In [15]: l
Out[15]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [16]: sum(i for i in l if i%2 == 0)
Out[16]: 20

5.2 传递值给生成器

《Functional Programming HOWTO》里“Passing values into a generator”一小节代码如下:

def counter (maximum):
    i = 0
	while i < maximum:
	    val = (yield i)
	    if val is not None:
		i = val
	    else:
		i += 1

可以这样调用:

In [48]: it = counter(10)

In [49]: it.next()
Out[49]: 0

In [50]: it.next()
Out[50]: 1

In [51]: it.next()
Out[51]: 2

In [52]: it.next()
Out[52]: 3

In [53]: it.send(8)
Out[53]: 8

In [54]: it.next()
Out[54]: 9

这里可以把yield放到表达式后面,调用者可以调用send方法传递一个值进去,如果没有传递的话,val默认是None

6 序列解构

>>> l = range(3)
>>> a,b,c = l
>>> a,b,c
(0, 1, 2)
>>> it = iter(l)
>>> a,b,c = it
>>> a,b,c
(0, 1, 2)
>>>

可迭代对象也可以把元素拆给变量。必须保证元素个数和变量数一致才可以。

7 内置变量

7.1 __debug__

是一个内置的常量,默认的值是True,如果为False,将去除代码中的assert语句。

但是不可以直接为它赋值为False,因为它是常量。只有用python -O执行的时候,它才为False

8 操作符

8.1 对象之间比较

In [1]: (1,2,3) < (4,5,6)
Out[1]: True

In [2]: (1,2,7) < (4,5,6)
Out[2]: True

In [3]: (4,2,7) < (4,5,6)
Out[3]: True

元组比较首先从序列第一个开始找,如果双方相等,就比较下一个元素,直到找到不等元素后,停止比较。也可用于列表等内置类型的比较。

9 I/O

9.1 缓存

如下代码:

import time

for i in xrange(5):
    print i,
    time.sleep(4)

你会发现变量i不是实时打印到终端的。这是因为print i,不会输出换行符,默认Linux的标准输出是缓存的,当遇到换行符或者达到一定大小之后,才会打印出来。stderr是不缓存的,一有内容则实时输出。

为了让代码实时输出,可以用sys.stdout.flush()函数

9.2 逐字节读取文件

with open('file') as f:
    for c in iter(lambda: f.read(1), ''):
	print c

9.3 如何获得文件大小

fstream = open('file','r')
fstream.seek(0,os.SEEK_END)    #将位置移到文件尾
fstream.tell()        #返回当前位置 就是文件尾 返回的数字就是文件的大小 单位字节

10 调试

10.1 跟踪函数调用栈

类似执行bash -x。

详细追踪:

python -m trace --trace script.py

显示调用了哪些函数:

python -m trace --trackcalls script.py

11 Python2、Python3兼容

11.1 导入库

Python3中很多内置包名已不和Python2中相同,比如实现队列的包,在Python2中叫“Queue”,Python3中已经规范为“queue”,在使用时可以通过异常捕获来兼容:

try:
    from Queue import Empty
    from Queue import Queue
except ImportError:
    from queue import Empty
    from queue import Queue

11.2 版本判断

if sys.version_info.major == 2:
    # Python2
elif sys.version_info.major == 3:
    # Python3

11.3 Python2调用print函数

print在Python3里已经成为一个函数了,如果要在Python2.7中完全把print当作函数使用,需要显示引用:

from __future__ import print_function

print(1, end='')
print(2, end='')