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语句应该按模块的字母
      顺序来排列。

另外,作者推荐了一个Python源码静态分析工具,它可以自动检查受测代码是否符合PEP8风格指南,而且还能找出Python程序里的多种常见错误。

第三条:了解bytes、str、和unicode的区别

之后主要使用Python3,在此对Python3进行讨论。

本条讲了Python3中有bytesstr这两种表示字符序列的类型。前者的实例包含原始的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的语法特性(写复杂的单行表达式),会使代码的可读性变差,不如将复杂的表达式移入辅助函数中,进行多次调用。

第五条:了解切割序列的方法

切割序列也就是切片操作,通常我们可以对liststrbytes进行切片,还可以延伸到对实现了__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))

未完待续...

posted @   zyfzjuer  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示