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来掌握内存的使用及泄露情况
本文来自博客园,作者:CestLaVie0019,转载请注明原文链接:https://www.cnblogs.com/fishwithsheep/p/17804792.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)