zl程序教程

您现在的位置是:首页 >  后端

当前栏目

《Effective Python》笔记 第一章-培养Pythonic思维

Python笔记 思维 第一章 Effective 培养
2023-09-14 08:56:52 时间

阅读Effective Python(第二版)的一些笔记。


原文地址:https://www.cnblogs.com/-beyond/p/15896478.html


第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'

缺点

  1. 左侧指定值的类型或者顺序发生变化时,可能发生类型不兼容的问题,需要校验两边的类型和数量一致
  2. 左侧的字符串模板和右侧的参数使用%拼接,如果参数过多时,代码就会比较长(混乱),需要拆分多行
  3. 左侧要重复同一个值多次,那么右侧需要重复同样的次数

str的format方法

>>> "name:{}, age:{}".format("abc", 99)
'name:abc, age:99'
  1. 与C风格的字符串格式化类似
  2. 同样需要保证左右两侧的参数保持一致

插值格式字符串

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

需要注意:

  1. zip函数在迭代的时候,只要其中一个集合遍历结束,那么就不再往下走了;
  2. 如果希望遍历时,以元素最多的集合为基准进行遍历,那么可以使用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块,他的功能有点绕,所以不要使用这种写法。

  1. 在for循环或者while循环从头到尾执行完没有中断,或者没有进入循环,就会执行else块代码;
data = [1, 2, 3]
for i in data:
    if i >= 4:
        print("循环发生中断,else代码块不会执行")
        break
else:
    print("循环从头到尾执行完毕了,接着执行else代码块")

# 循环从头到尾执行完毕或者未进入循环,接着执行else代码块
  1. 当循环执行过程中,发生了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变量

使用赋值表达式:

  1. 可以省一行代码;
  2. 压低变量的地位,明确wage的使用范围(注意外部其实还是可以访问到赋值表达式的变量)
  3. 可以模拟switch/case、do/while的写法``