《Effective Python》笔记 第一章-培养Pythonic思维
阅读Effective Python(第二版)的一些笔记。
第3条 了解bytes和str的区别
可以直接参考:https://sanyuesha.com/2016/11/06/python-string-unicode/
第4条 用支持插值的f-string取代C风格的格式字符串与str.format方法
C风格的字符串格式化
>>> "name:%s, age:%d" % ("abc", 99)
'name:abc, age:99'
缺点
- 左侧指定值的类型或者顺序发生变化时,可能发生类型不兼容的问题,需要校验两边的类型和数量一致
- 左侧的字符串模板和右侧的参数使用%拼接,如果参数过多时,代码就会比较长(混乱),需要拆分多行
- 左侧要重复同一个值多次,那么右侧需要重复同样的次数
str的format方法
>>> "name:{}, age:{}".format("abc", 99)
'name:abc, age:99'
- 与C风格的字符串格式化类似
- 同样需要保证左右两侧的参数保持一致
插值格式字符串
python 3.6支持的新特性,使用f
前缀,模板中可以直接使用{field_name}
来获取field_name的值,不用C风格那样在右侧指定参数。
>>> name="abc"
>>> age=99
>>> formatted_str = f"name:{name}, age:{age}"
>>> print(formatted_str)
name:abc, age:99
如果{field_name}的field_name没有定义,则报错NameError。
第5条 用辅助函数取代复杂的表达式
语法简洁的Python虽然可以写出很多浓缩的句式,比如if/else三元表达式、for/in列表生成式,lambda表达式,但应该避免让这样的写法把表达式弄得太复杂。
如果你发现表达式越写越复杂,那就应该考虑把它拆分成多个部分,并且把这套逻辑写到辅助函数里面。这样虽然要多编几行代码,但可以让程序更加清晰,所以总体来说还是值得的。
第6条 把数据结构直接拆分到多个变量里,不要专门通过下标访问
使用unpacking方式替换通过下标访问
data = [
("abc", 66, "beijing"),
("xyz", 88, "shanghai")
]
# 使用下标访问
for item in data:
name = item[0]
age = item[1]
addr = item[2]
print("name:{}, age:{}, addr:{}".format(name, age, addr))
# 通过unpacking方式
for item in data:
name, age, addr = item
print("name:{}, age:{}, addr:{}".format(name, age, addr))
使用enumerate + unpacking迭代列表
如果要遍历列表的时候,同时打印元素的index,那么习惯C风格的循环遍历列表的方式,一般会写为下面这样:
# 迭代-旧方式
size = len(data)
for index in range(0, size):
item = data[index]
print("index:%s, data:%s" % (index, item))
# 便捷方式
for index, item in enumerate(data):
print("index:%s, data:%s" % (index, item))
第7条 尽量用enumerate取代range
尽量避免使用for循环中使用下标访问集合元素,应改用enumerate进行迭代
data = [11, 22, 33, 44, 55]
# 使用下标形式访问
for i in range(len(data)):
print("index:{}, value:{}".format(i, data[i]))
# index:1, value:11
# index:2, value:22
# 使用enumerate进行迭代,起始序号为0
for i, val in enumerate(data):
print("index:{}, value:{}".format(i, val))
# index:1, value:11
# index:2, value:22
# 起始需要为4
for i, val in enumerate(data, 4):
print("index:{}, value:{}".format(i, val))
# index:4, value:11
# index:5, value:22
第8条 用zip函数同时遍历两个迭代器
有时候,需要同步遍历两个列表进行操作时,下面是比较简单的做法:
data_1 = [1, 2, 3]
data_2 = ["x", "y", "z"]
for i in range(len(data_1)):
item_1 = data_1[i]
item_2 = data_2[i]
print("item_1:{}, item_2:{}".format(item_1, item_2))
# item_1:1, item_2:x
# item_1:2, item_2:y
# item_1:3, item_2:z
另外一种比较方便的方式就是使用zip(),zip函数可以接受多个可迭代的集合,然后每次迭代都会从这些集合中取一个元素,示例如下:
for item_1, item_2 in zip(data_1, data_2):
print("item_1:{}, item_2:{}".format(item_1, item_2))
# item_1:1, item_2:x
# item_1:2, item_2:y
# item_1:3, item_2:z
需要注意:
- zip函数在迭代的时候,只要其中一个集合遍历结束,那么就不再往下走了;
- 如果希望遍历时,以元素最多的集合为基准进行遍历,那么可以使用itertools模块的zip_longest()函数。
示例如下:
data_3 = [1, 2, 3, 4]
data_4 = ["x", "y", "z"]
for item_1, item_2 in zip(data_3, data_4):
print("item_1:{}, item_2:{}".format(item_1, item_2))
# item_1:1, item_2:x
# item_1:2, item_2:y
# item_1:3, item_2:z
from itertools import izip_longest # python2
# from itertools import zip_longest # python3
for item_1, item_2 in izip_longest(data_3, data_4):
print("item_1:{}, item_2:{}".format(item_1, item_2))
# item_1:1, item_2:x
# item_1:2, item_2:y
# item_1:3, item_2:z
# item_1:4, item_2:None
第9条 不要在for与while循环后面写else块
python中支持for循环和while循环后面加else块,他的功能有点绕,所以不要使用这种写法。
- 在for循环或者while循环从头到尾执行完没有中断,或者没有进入循环,就会执行else块代码;
data = [1, 2, 3]
for i in data:
if i >= 4:
print("循环发生中断,else代码块不会执行")
break
else:
print("循环从头到尾执行完毕了,接着执行else代码块")
# 循环从头到尾执行完毕或者未进入循环,接着执行else代码块
- 当循环执行过程中,发生了break或者return,循环被中断了,那么else就不会执行;
# 修改数据,让循环发生中断
data = [1, 2, 3, 4, 5]
for i in data:
if i >= 4:
print("循环发生中断,else代码块不会执行")
break
else:
print("循环从头到尾执行完毕或者未进入循环,接着执行else代码块")
# 循环发生中断,else代码块不会执行
第10条 用赋值表达式减少重复代码
代码中对于某个变量进行判断操作是非常频繁的,有时候这个变量其实没有多大意义,只是为了确定走哪个分支,仅仅用在判断或者仅仅用在if/else里面;
比如下面判断工资wage属性,进行不同的操作
person = Person("小明", "male", 50000)
# 直接使用属性
if person.wage > 10000:
print("wage:{}w".format(person.wage / 10000))
else:
print("wage:{}k".format(person.wage / 1000))
# 将属性赋值给变量,后面可以直接会用变量
wage = person.wage
if wage > 10000:
print("wage:{}w".format(wage / 10000))
else:
print("wage:{}k".format(wage / 1000))
# 其他代码,并且wage变量未使用过
直接使用属性的的形式,当参数名称比较长的时候不太方便;
将属性赋值给一个变量,可能会让人觉得wage变量是一个很重要的变量,实际上可能只是用来判断走哪个分支,后面根本没有用过。
Python 3.8的一个新特性:赋值表达式,使用var_name := value
这种形式进行赋值,用法如下:
if (wage := person.wage) > 10000:
print("wage:{}w".format(wage / 10000))
else:
print("wage:{}k".format(wage / 1000))
print(wage) # 仍旧能访问到wage变量
使用赋值表达式:
- 可以省一行代码;
- 压低变量的地位,明确wage的使用范围(注意外部其实还是可以访问到赋值表达式的变量)
- 可以模拟switch/case、do/while的写法``