Effective Python 编写高质量Python代码的59个有效方法----读书笔记

第2条 遵循PETP8风格指南

PEP8指南 Python Enhancement Proposal #8

  • 使用space(空格)来表示缩进,而不要用tab(制表符)
  • 和与法相关的每一层缩进都用4个空格来表示
  • 每行的字符数不应超过79
  • 对于占据多行的长表达式来说,除了首行之外的其余各行都应该在通常的缩进级别至上再加4个空格
  • 文件中的函数与类应该用两个空行隔开
  • 在同一个类中,个方法之间应该用一个空行隔开
  • 在使用下表来获取列表元素、调用函数或给关键字参数赋值的时候,不要在两旁添加空格
  • 为变量赋值的时候,赋值符号的左侧和右侧应该各自写上一个空格,而且只写一个就好

命名

  • 函数、变量及属性应该用小写字母来拼写,各单词之间以下划线相连,例如:lowercase_underscore
  • 受保护的实例属性,应该以但个下划线开头,例如,_leading_underscore
  • 私有的实例属性,应该以两个下划线开头,例如,__double_leading_underscore
  • 类与异常,应该以每个单词首字母均大写的形式来命名,例如,CapitalizedWord
  • 模块级别的常量,应该全部采用大写字母来拼写,各单词之间以下划线相连,例如,ALL_CAPS
  • 类中的实例方法(instance method),应该把首个参数命名为self,以表示该对象自身
  • 类方法(class method)的首个参数,应该命名为cls以表示该类自身

表达式和语句

  • 采用内联形式的否定词,而不要把否定词放在整个表达式前,例如,if a is not b(√),而不是if not a is b(×)
  • 不要通过检测长度的办法(如if len(somelist)==0)来判断somelist是否为[]或''等空值,而是应该采用if not somelist的写法来判断,会假定:空值将自动评估为False
  • 不要编写单行的if语句、for循环、while循环以及except复合语句,而是应该分成多行
  • import语句写在文件开头,推荐使用绝对名称,相对名称来导入时,采用明确的写法,例如,from * import foo
  • import语句应该按照顺序分成三个部分:标准库模块、第三方模块以及自用模块;每一部分,按照各模块的字母顺序来排列

第3条 了解bytes、str与unicode的区别

Unicode->二进制数据,encode
二进制数据->Unicode,decode
Python3:
bytes--->原始8位值;str--->Unicode字符;
Python2:
str--->原始8位值;unciode--->Unicode字符;

  • 从文件中读取二进制数据,或向其中写入二进制数据时,总应该以'rb'或'wb'等二进制模式来开启文件

第4条 用辅助函数来取代复杂的表达式

  • 复杂的表达式建议移入辅助函数之中,如果需要反复使用相同的逻辑,更应如此
  • if/else表达式,比or/and的布尔操作符携程的表达式更清晰

第5条 了解切割序列的办法

a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
a[:] # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
a[:5] # ['a', 'b', 'c', 'd', 'e']
a[:-1] # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
a[4:] # ['e', 'f', 'g', 'h']
a[-3:] # ['f', 'g', 'h']
a[2:5] # ['c', 'd', 'e']
a[2:-1] # ['c', 'd', 'e', 'f', 'g']
a[-3:-1] # ['f', 'g']
# somelist[-0:]则成了原列表的一份拷贝
  • 不要写多余的代码,start为0、end为序列长度时,应省略
  • 切片操作不会计较start与end索引是否越界
  • 对list赋值时,如果使用切片操作,值会在相关范围替换成新值,长度不同也能替换

第6条 在单次切片操作内,不要同时指定start、end和stride

  • 尽量使用stride为正的情况,且不带start与end索引的娥切割操作,stride尽量避免用负数
  • 需要同时使用start、end、stride,考虑拆分成两条语句,或使用内置的itertools模块中的islice

第7条 用列表推导来取代map和filter

列表推导list comprehension,可以根据一份列表来制作另外一份

a = [1, 2, 3, 4, 5, 6]
squares = [x**2 for x in a]
print(squares)
>>>
[1, 4, 5, 16, 25, 36]
# 等价于
squares = map(lambda x: x**2, a)
# using filter
even_squares = [x**2 for x in a if x % 2 == 0]
print(even_squares)
>>>
[4, 16, 36]
# 等价于
alt = map(lambda x: x**2, filter(lambda x: x % 2 == 0, a))
assert even_squares == list(alt)
  • 列表推导比map和filter更清晰
  • 列表推导可以跳过列表中的某些元素,如果用map,则需要辅之以filter
  • 字典与集也支持推导表达式

第8条 不要使用含有两个以上表达式的列表推导

# 使用多重循环
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]
# 每个元素平方后构成新矩阵
squared = [[x**2 for x in row] for row in matrix]
print(squared)
>>>
[[1, 4, 9], [16, 25, 36], [49, 64, 81]]
# 如果表达式还存在一层循环,列表推导则极长,考虑到可阅读行,需要分行
my_lists = [
[[1, 2, 3], [4, 5, 6]],
#...
]
flat = [ x for sublist1 in my_lists
for sublist2 in sublist1
for x in sublist2]
# 简洁性不如for循环
flat = []
for sublist1 in my_lists:
for sublist2 in sublist1:
flat.extend(sublist2)
  • 列表推导支持多级循环,每一级循环也支持多项条件
  • 为更好理解,尽量避免写超过两个表达式的列表推导

第9条 用生成器表达式来改写数据量较大的列表推导

生成器表达式generator expression,是列表推导和生成器的一种泛化generalization

# 数据量少使用
value = [len(x) for x in open('/tmp/my_file.txt')]
print(value)
# 数据量多使用迭代器
it = (len(x) for x in open('/tmp/my_file.txt')])
print(it)
>>>
<generator object <genexpr> at 0xxxxxxxxxx>
  • 数据量较大时,列表推导会因为占用太多内存而出问题,生成器表达式所返回的迭代器,可以主次产生输出值,避免内存用量的问题
  • 生成器表达式所返回的娥迭代器,可以供另一个生成器表达式的for子表达式使用(理解为可联合嵌套使用)
  • 串在一起的生成器表达式执行速度很快

第10条 尽量用enumerate取代range

flavor_list = ['vanilla', 'chocolate', 'pecan', 'strawberry']
# 使用字符串列表
for flavor in flavor_list:
print('%s is delicious' % flavor)
# 当前元素的索引,使用range
for i in range(len(flavor_list)):
flavor = flavor_list[i]
print('%d: %s' %(i+1, flavor))
# 使用内置enumerate函数
for i, flavor in enumerate(flavor_list):
print('%d: %s' %(i+1, flavor)
# enumerate可以指定开始计数时所用的值
for i, flavor in enumerate(flavor_list, 1):
print('%d: %s' %(i, flavor)

第11条 用zip函数同事遍历两个迭代器

names = ['Cecilia', 'Lise', 'Marie']
letters = [len(n) for n in names]
# 找到最长名字
longest_name = None
max_letters = 0
for i in range(len(names)):
count = letters[i]
if cout > max_letters:
longest_name = names[i]
max_letters = cout
print(longest_name)
# 使用enumerate方法,复写for循环
for i, name in enumerate(names):
count = letters[i]
if count > max_letters:
longest_name = name
max_letters = count
# zip可以把两个及以上的迭代器封装成生成器,zip生成器,会从每个迭代期中获取该迭代器的下一个值,汇聚成元组tuple
# 用zip方法复写for循环
for name,count in zip(names, letters):
if cout > max_letters:
longest_name = name
max_letters = count

zip函数的问题:

  • python2 的zip不是生成器,如需在python2用zip遍历数据量大的迭代器,应使用itertools内置模块的izip
  • 如果输入的迭代器长度不同,则会有异常结果
    注意点:
  • zip函数可以平行地遍历多个迭代器
  • python3的zip相当于生成器,在遍历过程中逐次产生元组,python2中的zip则是将元组完全生成好,并一次性地返回整份列表
  • 迭代器长度不等,zip会自动提前终止
  • itertools内置模块的zip_longest的函数可以平行地遍历多个迭代器,无需考虑迭代器的长度是否相等

第12条 不要再for和while循环后面写else块

  • python的特殊语法,可在for及while循环的内部语句块后使用else块;当所有语句都没有遇到break时,循环后面的else才执行,不建议使用

第13条 合理利用try/except/else/finally结构中的每个代码块

第59条 用tracemalloc来掌握内存的使用及泄露情况

posted @   CestLaVie0019  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示