第1条
pass
第2条:遵循PEP8风格指南
- 使用space(空格)来表示缩进,而不要用tab
- 和语法相关的每一层都用4个空格来表示
- 每行的字符数不超过79
- 受保护的实例属性,应该以单个下划线开头
- 私有的实例属性,应该以两个下划线开头
- 类与异常,应该以每个单词首字母均大写的形式来命名
- 模块级别的常量,应该全部采用大写字母来拼写,各单词之间以下划线相连
- if a is not b 而不是 if not a is b
- 不要通过检测长度的方法(if len(somelist) == 0)来判断somelist是否为空,而是采用if not somelist来判断。
第3条:了解bytes, str 与unicode的区别
bytes是给计算机看的,str和unicode是给人看的。
- Python3字符序列的两种表示为byte和str。前者的实例包含原始的8位值,即原始的字节;后者的实例包括Unicode字符。
- 要想把Unicode字符转换为二进制数据,就必须使用encode方法。要想把二进制数据转换成Unicode字符,则必须使用decode方法。
- 在Python3中,bytes是一种包含8位值的序列,str是一种包含Unicode字符的序列。开发者不用比较操作来混合处理。
def to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes):
return bytes_or_str.decode('utf-8')
else:
return bytes_or_str
def to_bytes(bytes_or_str):
if isinstance(bytes_or_str, bytes):
return bytes_or_str.encode('utf-8')
else:
return bytes_or_str
第4条:用辅助函数来取代复杂的表达式
pass
第5条:了解切割序列的办法
- 切片操作不会计较start和end索引是否越界。如果越界,则去边界值,而不会取越界值。
第6条:在单词切片操作内,不要同时指定start,end和stride
步进式切片:somelist[start : end : stride]
a = [1, 2, 3, 4, 5, 6]
# 取奇数
odds = a[::2]
#取偶数
evens = a[1::2]
传入stride = -1,能够把字节形式存储的字符串反转过来。
x = b'mongoose'
y = x[::-1]
这种技巧对字节串和ASCII字符有用,但是对已经编码成UTF-8字节串的Unicode字符无效。
w = '谢谢'
x = w.encode('utf-8')
y = x[::-1]
z = y.decode('utf-8')
>> z = y.decode('utf-8') UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa2 in position 0: invalid start byte
为了让代码便于阅读,不要同时把start,end, stride同时写在一起。使用步进式切片,把切割结果赋值给某个变量,然后再进行二次切割
a = ['a','b','c','d','e','f','g','h']
b = a[::2] # ['a','c','e','g']
c = b[1:-1] # ['c','e']
如果程序对执行时间或内存用量要求非常严格,以致不能采用两阶段切割法,可以考虑考虑使用Python 内置的itertools模块,该模块中有个islide方法,这个方法不允许为start, end或stride指定负值。(第46条)、
第7条:用列表推导来取代map和filter
列表推导:根据一份列表来制作另外一份
用列表推导式来写代码,会更简洁,不过根据正月点灯笼的讲解,使用map运行会更块,在数据量非常巨大的时候有着量级的优势。
代码:
构建一个列表的元素平方列表
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 列表推导式
squares = [x**2 for x in a]
# map方法
squares = map(lambda x: x ** 2, a)
如果外加一个限制条件,比如说元素能被2整除才会进行平方操作:
# 列表推导式
even_squares = [x ** 2 for x in a if x % 2 == 0]
# map方法与filter的结合
alt = map(lambda x: x**2, filter(lambda x: 2 % 2 == 0, a))
assert even_squares == list(alt)
第8条:不要使用含有两个以上表达式的列表推导
两层列表推导。这个功能采用包含两个for表达式的列表推导即可实现,这些for表达式会按照从左至右的顺序来评估。
matrix = [[1,2,3],[4,5,6],[7,8,9]]
flat = [x for row in matrix for x in row]
>>> [1,2,3,4,5,6,7,8,9]
对二维矩阵中的每个单元格取平方,然后用这些平方值构建新矩阵。
squared = [[x**2 for x in row] for row in matrix]
>>>[[1, 4, 9], [16, 25, 36], [49, 64, 81]]
列表推导也支持多个if条件。
a = [1,2,3,4,5,6,7,8,9,10]
b = [x for x in a if x > 4 if x % 2 == 0]
c = [x for x in a if x > 4 and x % 2 == 0]
第9条:用生成器表达式来改写数据量较大的列表推导
列表推导式缺点:当数据非常多的时候,会消耗大量的内存。
生成器:是对列表推导和生成器的一种泛化。生成器表达式在运行的时候,并不会把整个输出序列都呈现出来,这个迭代器每次可以根据生成器表达式产生一项数据。
# 列表推导式:
value = [len(x) for x in open('/tmp/my_file.txt')]
print(value)
>>> [100, 57, 15, 1, 12, 75, 5, 86, 89, 11]
# 生成器表达式:
value = (len(x) for x in open('/tmp/my_file.txt'))
print(value)
>>> <generator object <genexpr> at 0x101b81480>
生成器调用next方法就可以输出下一个值。
套娃生成器:让生成器相互结合。把一个生成器 表达式 所返回的 迭代器 用作 另外一个 生成器表达式的输出值
it = (len(x) for x in open('/tmp/my_file.txt'))
roots = ((x, x**0.5) for x in it)
第10条:尽量用enumerate 取代range
enumerate可以把各种迭代器包装成生成器。每次产生一对值,第一个值为循环下标,第二个为从迭代器中获取到的下一序列元素
for i, flavor in enumerate(flavor_list):
print('%d: %s' % (i, flavor))
>>>
>0: vanilla
>1:chocolate
>2:pecan
>3:strawberry
指定计数的起始值
for i, flavor in enumerate(flavor_list, 3):
print('%d: %s' % (i, flavor))
>>>
>3: vanilla
>4:chocolate
>5:pecan
>6:strawberry
第11条:用zip函数同时遍历两个迭代器
Python3中的zip
函数,可以把两个或两个以上的迭代器封装为生成器。这种zip生成器,会从每个迭代器中获取迭代器的下一值,然后把这些值汇聚成元组。
需要注意zip的多个迭代器的长度是否统一。
不要在for和while循环后面写else块
for i in range(3):
print(i)
else:
print("else block")
>>>
0
1
2
else block
含义:循环里用break语句提前跳出,会导致程序不执行else块。
对比
for i in range(3):
print(i)
if i == 1:
break
else:
print("else block")
>>>
0
1
总结:不要在for和while循环后面写else块
第13条:合理利用try/except/else/finally结构中的每一个代码块
1.finally块
try – finally模块。执行try后,总是执行finally块。