Effective Python

编辑时间:2018.9.18

有一部分是原书的内容,但测试样例全都是使用Python3.6.6自己测试的,Python的版本在不断的优化,因此语法在变化优化,本文有时效性大概半年吧。


一、用Python方式来思考

pep8
切割序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
list_ = [1, 2, 3, 4, 5]
print(list_[:2]) # 前两个
print(list_[:-2]) # 去掉后两个
print(list_[-2:]) # 后两个
print(list_[2:]) # 去掉前两个
print(list_[-3:3]) # 前三个和后三个的交集
print(list_[2:-2]) # 去掉前两个和后三个
assert list_[2:] == list_[2:len(list_)] # assert是断言的意思,这里是断言两者相等为真,如果不相等就会报错

# result
# [1, 2]
# [1, 2, 3]
# [4, 5]
# [3, 4, 5]
# [3]
# [3]
在单次切片操作内,不要同时指定start、end和stride
  • somelist[start : end : stride]
1
2
3
4
5
6
list_ = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list_[::2]) # 序列长度为n时,取n/2个值,每次取未取序列中的第2个值
print(list_[::1]) # 序列长度为n时,取n/1个值,每次取未取序列中的第1个值
print(list_[::-1]) # 序列长度为n时,取n/1个值,序列先倒序,然后每次取未取序列中的第1个值
print(list_[::-2]) # 序列长度为n时,取n/2个值,序列先倒序,然后每次取未取序列中的第2个值
print(list_[2:6:2]) # 序列长度为n时,先取出序列的第3个到第7个值,然后每次取未取序列中的第2个值
用列表推导来取代map和filter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
list_ = [1, 2, 3, 4, 5]

list_1 = [i**2 for i in list_] # 列表推导
print(list_1)

list_2 = map(lambda x: x**2, list_) # map函数
print(list_2)
print(type(list_2))
print(list_)

list_3 = [x**2 for x in list_ if x % 2 == 0]
print(list_3)

list_4 = map(lambda x: x**2, filter(lambda x: x % 2 == 0, list_))
print(list_4)

dict_ = {'a': 1, 'b': 2, 'c': 3}
dict_1 = {rank: name for name, rank in dict_.items()}
print(dict_1) # 字典中,值和键交换

dict_2 = {1: 'hello', 2: 'world', 3: '!'}
set_1 = {len(strings) for strings in dict_2.values()}
print(set_1)


# result
# [1, 4, 9, 16, 25]
# <map object at 0x00000254CB441208>
# <class 'map'>
# [1, 2, 3, 4, 5]
# [4, 16]
# <map object at 0x00000254CB4413C8>
# {1: 'a', 2: 'b', 3: 'c'}
# {1, 5}
不要使用含有两个以上表达式的列表推导,这样可行但是会减少代码的可读性。可以使用多次单个表达式的列表推导来代替多个表达式的列表推导。
使用生成器来改写数据量较大的列表推导
1
2
3
4
5
6
7
8
9
10
11
12
13
list_ = [1, 2, 3, 4, 5]

root = ((x, x**2) for x in list_)

print(next(root))
print(next(root))
print(next(root))


# result
# (1, 1)
# (2, 4)
# (3, 9)
尽量使用enumerate替代range
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
list_ = [1, 2, 3, 4, 5]

for i in range(len(list_)):
print(list_[i])

for i, value in enumerate(list_):
print(i, ': ', value)


# result
# 1
# 2
# 3
# 4
# 5
# 0 : 1
# 1 : 2
# 2 : 3
# 3 : 4
# 4 : 5
使用zip函数同时遍历多个迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
list_1 = [1, 2, 3]
list_2 = ['a', 'b', 'c']
list_3 = [10, 20, 30]

for number, string in zip(list_1, list_2):
print(number, string)


list_1.append(4)
print(list_1)
for number, string in zip(list_1, list_2):
print(number, string)


for a_, b_, c_ in zip(list_1, list_2, list_3):
print(a_, b_, c_)


# result
# 1 a
# 2 b
# 3 c
# [1, 2, 3, 4]
# 1 a
# 2 b
# 3 c
# 1 a 10
# 2 b 20
# 3 c 30

二、函数

尽量使用异常来表示特殊情况,而不要返回None(捕捉异常)
考虑用生成器来改写直接返回列表的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def add_():
a, b = 1, 1
while True:
c = a + b
yield c
a, b = b, c


sum_ = add_()

if __name__ == '__main__':
for i in range(10):
print(next(sum_), end=', ')


# result
# 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,

这里的函数的返回值由yield决定

三、类与继承

四、元类及属性

五、并发及并行

  • Python的多线程、多进程、协程貌似不太好,使用多进程时要特别注意多进程结束后各个进程的执行顺序
  • 用subprocess模块来管理子进程
  • 可以用线程来执行阻塞式I/O,但不要用它做计算
  • Python的GIL是一把互斥锁,防止抢占式多进程切换操作的干扰。因此同一时刻只有一条线程在跑,解决办法就是使用其他语言来写多进程的操作,使用Python来调用
  • 使用Queue来协调各个线程之间的工作
    • Python的解释器对于线程结束后的操作一直是混乱的,这里自己可以使用Python的内置Queue函数来协调
  • 考虑使用协程来并发地运行多个函数
  • Python中,核心、线程、进程、子进程和协程,协程之家切换代价最小
  • Python的协程没有锁,这个比进程好用,但要小心不要产生死循环(自由的代价)
  • 考虑使用费concurrent.futures来实现并行计算

六、内置模块

  • 使用datetime模块来处理本地时间,而不是time模块
    • time模块是当前时间减去很久前的一个固定时间,单位是秒
使用Python内置的算法与数据结构
  1. 双向队列
    • collection模块中的deque类
  2. 有序字典
    • collection模块中的OrderedDict类
  3. 带有默认值的字典
    • collection模块中的defaultdict类
  4. 堆队列(优先队列)
    • heapq模块
  5. 二分查找
    • bisect模块中bisect_left等函数
  6. 与迭代器有关的工具
    • 内置的itertools模块

七、协作开发

  • 为自编的模块定义根异常
  • 书中建议使用虚拟环境隔离项目,本人多次搭建虚拟环境,但觉得还是正常环境就好,或者使用docker

八、部署

  • repr字符串来输出调试信息
  • 使用tracemalloc来测试内存泄漏情况

个人总结

  • 这貌似是本人有史以来读的第一本老美比国人写的糟的书,也有可能是年代久远吧~~~
  • 对于性能的优化,使用CPython应该比较好吧,Cython就是一门新的语言,感觉语法很无语。或者使用SWIG或者ctype模块调用c语言代码。
  • Python的并行方面真的做的不够好吧
Donate comment here