Effective Python 读书笔记一
Effective Python读书笔记一
前言
《Effective Python.编写高质量Python代码的59个有效方法》的作者是Brett Slatkin,这本书旨在向读者讲解如何以Pythonic(符合Python风格)的方式来编写程序。可以作为字典一样查阅,也可以像普通图书一样阅读。
Effective一词,并不单单局限于执行速度层面的高效率,同时有着令代码易于阅读、易于测试且易于维护等意思,此外,还蕴含这易于扩展,易于修改和易于多人协作等高阶的概念。
我写这个读书笔记的想法是结合工作中使用Python的经验,记录59个有效方法中有重要记录价值以及对我有较大启发的内容。希望阅读后在以后的工作中能够提高Python编码效率。同时使代码更加易读、易维护、易扩展。
第1章 用Pythonic方式思考
什么叫pythonic方式?其实就是Python语言的最佳编程习惯。
书中解释说:对C++或Java等其他语言比较熟悉的人,可能还在按自己喜欢的风格来使用Python;而刚刚接触Python的程序员,则需要逐渐熟悉许多可以用Python代码来表达的概念。但无论哪一种开发者,都必须知道如何以最佳方式完成常见的Python编程工作,这种最佳方式,就是Pythonic方式。该方式将会影响你所写的每个程序。
第一条:确认自己所用的python版本
这条讲python2和python3两个版本一些基本的区别和未来的开发和维护方向。主要的内容是:
- python2的功能开发已经冻结,只会进行bug修复,安全增强以及移植的工作。
- python3会经常增加新功能并提供改进。并且大部分python开源源码库已经支持Python3。因此作者建议使用Python3来进行新项目的开发。
第二条:遵循PEP8风格指南
《Python Enhancement Proposal#8》 (8号Python增强提案)又叫PEP8,它是针对Python代码格式而编订的风格指南。尽管可以在保证语法正确的前提下随意编写Python代码,但是,采用一致的风格来书写可以令代码更加易懂、更加易读。
文中提到如下几点,有比较记录一下
-
空白
- 使用space(空格)来表示缩进,而不要用tab(制表符)。
- 和语法相关的每一层缩进都用4个空格来表示。
- 每行的字符数不应超过79。pycharm设置代码行的长度为79字符(PEP8)
- 对于占据多行的长表达式来说,除了首行之外的其余各行都应该在通常的缩进
级别之上再加4个空格。 - 文件中的函数与类之间应该用两个空行隔开。
- 在同一个类中,各方法之间应该用一个空行隔开。
- 在使用下标来获取列表元素、调用函数或给关键字参数赋值的时候,不要在两
旁添加空格。 - 为变量赋值的时候,赋值符号的左侧和右侧应该各自写上一个空格,而且只写
一个就好。
-
命名
- 函数、变量及属性应该用小写字母来拼写,各单词之间以下划线相连,例如,
lowercase_ underscore。 - 受保护的实例属性,应该以单个下划线开头,例如,_leading underscore。
- 私有的实例属性,应该以两个下划线开头,例如,_double_leading _underscore。
- 类与异常,应该以每个单词首字母均大写的形式来命名,例如,CapitalizedWord。
- 模块级别的常量,应该全部采用大写字母来拼写,各单词之间以下划线相连,
例如,ALLCAPS。 - 类中的实例方法(instance method),应该把首个参数命名为self,以表示该对象自身。
- 类方法(class method)的首个参数,应该命名为cls,以表示该类自身。
- 函数、变量及属性应该用小写字母来拼写,各单词之间以下划线相连,例如,
-
表达式和语句
- 不要通过检测长度的办法(如if len(somelist)==0)来判断somelist是否为[]
或"等空值,而是应该采用if not somelist这种写法来判断,它会假定:空值将
自动评估为False。 - 检测somelist是否为[1]或'hi'等非空值时,也应如此,if somelist语句默认会把
非空的值判断为True。 - 不要编写单行的if语句、for循环、while循环及except复合语句,而是应该把
这些语句分成多行来书写,以示清晰。 - import语句应该总是放在文件开头。
- 引入模块的时候,总是应该使用绝对名称,而不应该根据当前模块的路径来
使用相对名称。例如,引人bar包中的foo模块时,应该完整地写出from bar
import foo,而不应该简写为import foo。 - 如果一定要以相对名称来编写import语句,那就采用明确的写法:from.import foo。
- 文件中的那些import语句应该按顺序划分成三个部分,分别表示标准库模块、
第三方模块以及自用模块。在每一部分之中,各import语句应该按模块的字母
顺序来排列。
- 不要通过检测长度的办法(如if len(somelist)==0)来判断somelist是否为[]
另外,作者推荐了一个Python源码静态分析工具,它可以自动检查受测代码是否符合PEP8风格指南,而且还能找出Python程序里的多种常见错误。
第三条:了解bytes、str、和unicode的区别
之后主要使用Python3,在此对Python3进行讨论。
本条讲了Python3中有bytes
和str
这两种表示字符序列的类型。前者的实例包含原始的8位值,后者的示例包含Unicode字符。这句话说的不是很清晰,可以参考浅析Python3中的bytes和str类型 - Chown-Jane-Y - 博客园 (cnblogs.com)
关于Unicode、UTF-8等编码方式,阮一峰有篇博客讲的比较通俗易懂:字符编码笔记:ASCII,Unicode 和 UTF-8 - 阮一峰的网络日志 (ruanyifeng.com)
在这一条中,作者强调要把编码和解码的操作放在界面最外围来做,程序的核心部分应该使用Unicode字符类型。另外作者给了两个helper函数,以便是字符类型在bytes和str之间进行转换:
- 接收bytes或str,总是返回str
def to_str(bytes_or_str):
if isinstance(bytes_or_str,bytes):
value = bytes_or_str.decode('utf-8')
else:
value = bytes_or_str
return value
- 接收bytes或str,总是返回bytes
def to_bytes(bytes_or_str):
if isinstance(bytes_or_str, str):
value = bytes_or_str.encode('utf-8')
else:
value = bytes_or_str
return value
最后一点需要关注的是,在Python3中,如果要将数据写入文件或者从文件中读取数据,如果是对bytes进行操作,需要使用wb
以及rb
模式,否则会报TypeError。
with open('/tmp/test.bin','wb') as f:
f.write(os.urandom(10))
第四条:使用辅助函数来取代复杂的表达式
这条是讲,如果开发者过度运用Python的语法特性(写复杂的单行表达式),会使代码的可读性变差,不如将复杂的表达式移入辅助函数中,进行多次调用。
第五条:了解切割序列的方法
切割序列也就是切片操作,通常我们可以对list
、str
、bytes
进行切片,还可以延伸到对实现了__getitem__
和__setitem__
这两个内置方法的Python类进行切片。
基本语法不再讨论。没有什么特别值得关注的点。
第六条:在单次切片操作内,不要同时指定start、end和stride
没有特别值得关注的点。
其中讲到了序列反转的一个用法(对已经编码成UTF-8字节串的Unicode字符无效):
a= '123'
a[::-1]
第七条:用列表推导来取代map和filter
Python内置的map和filter我没有用过,但是列表推导以及dict和set的列表推导式倒是经常使用。
下面记录这几种推导式:
# 列表推导
a = [1,2,3,4]
squares = [x**2 for x in a]
# 字典推导
fruit_dict = {banana:1,apple:2,orange:3}
rank_dict = {rank:name:for name,rank in fruit_dict.items()}
第八条:不要使用含有两个以上的表达式的列表推导
如题,原因是为了便于代码阅读和理解
第九条:用生成器表达式来改写数据量较大的列表推导
这一条主要是为了解决一个问题:如果列表推导过程中,输入的数据很多的话,会消耗大量内存,并且导致程序运行时间变慢。
典型场景——读取文件
value = [len(x) for x in open('/tmp/file.txt')]
解决方式是使用生成器表达式,即:
it = (len(x) for x in open('/tmp/file.txt'))
print(next(it))
# 或者 it.__next__()
生成器表达式经常用于处理列表中存储大量数据,导致内存大量消耗的问题。生成器的简单介绍见Python生成器详解 (biancheng.net),生成器本质上是特殊的迭代器。使用生成器表达式会返回一个迭代器,而不会处理文件内容,可以逐次产生输出值,从而减少了内存的占用。
第十条:尽量用enumerate取代range
这条的使用场景就是想要在迭代列表时,还想要拿到列表的索引,那么就可以使用enumerate
。
作者给了一个用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,1)
print('%d:%s' % (i , flavor))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!