前言
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。
编写干净的,Python式的代码就是要尽可能地使其易于理解,但又要简明扼要。以下是有关Python重构的系列文章的第一部分,其它部分会继续推出。本系列的重点是为什么这样做是好的做法,而不仅仅是教你如何做。
1. 合并嵌套的if条件
太多的嵌套使代码难以理解,尤其在python中,因为Python中没有使用括号来描述不同的嵌套级别。阅读多层嵌套的代码令人困惑,因为必须跟踪哪些条件与哪些级别相关。因此,我们应尽量减少嵌套。使用and可以很容易地将两个if条件结合在一起。
Before:
if a:
if b:
return c
After:
if a and b:
return c
2. 合并条件语句的重复代码
我们应当一直寻找去除重复代码的方法。代码提升则是一种很好的方式。
有时,部分代码在条件语句的两个分支上完全相同。这意味着代码将始终执行。应将重复的代码提升到条件语句外。
if sold > DISCOUNT_AMOUNT:
total = sold * DISCOUNT_PRICE
label = f'Total: {total}'
else:
total = sold * PRICE
label = f'Total: {total}'
通过在条件语句之外给label赋值,我们删除了重复的一行代码,并使条件语句中total的内容更加清晰。
if sold > DISCOUNT_AMOUNT:
total = sold * DISCOUNT_PRICE
else:
total = sold * PRICE
label = f'Total: {total}'
3. 用yield from替换for循环中的yield
一个经常被忽略的小技巧是,Python的yield关键字对于集合有一个对应的yield from,因此不需要使用for循环遍历集合。这使得代码稍微短一些,并删除了for循环额外的变量和mental overhead。消除for循环还可以使yield from版本的代码快15%左右。。
Before:
def get_content(entry):
for block in entry.get_blocks():
yield block
After:
def get_content(entry):
yield from entry.get_blocks()
4. 使用any()代替for循环
一个常见的模式是,我们需要找出某个条件是否适用于集合中的一个或所有项。可以通过如下的for循环实现:
found = False
for thing in things:
if thing == other_thing:
found = True
break
一种更简洁的方式是使用Python的内置函数any()和all()来更清楚地实现。
found = any(thing == other_thing for thing in things)
any()将在至少一个元素求值为True时返回True, all()仅在所有元素求值为True时返回True。
如果可能,这些也会短路执行。如果对 any() 的调用找到一个值值为 True 的元素,它可以立即返回。如果代码还没有短路,这可能会导致性能改进。
5. 将list()替换为[]
创建列表最简洁也最符合python风格的方法是使用[]符号。
l = []
这符合我们用元素创建列表的方式,节省了一些精力。节省的精力可用于思考创建list的两种不同的方法。
l = ['first', 'second']
这样做还有一个额外的好处就是性能的提升,以下就是更改前后的时间对比:
$ python3 -m timeit "l = list()"
5000000 loops, best of 5: 63.3 nsec per loop
$ python3 -m timeit "l = []"
20000000 loops, best of 5: 15.8 nsec per loop
用{}替换dict()也会产生类似的推理和性能结果。
6. 从for/while循环中提升语句
另一种提升代码的方式是将不变式语句从循环中取出。如果一个语句只是为循环中使用设置了一些变量,那么它不需要在循环中。循环本身就很复杂,所以在编写循环时,应该考虑让它们更短、更容易理解。
在本例中,city变量在循环中被赋值,但只读取它而不修改它。
for building in buildings:
city = 'Beijing'
addresses.append(building.street_address, city)
因此,把它提出来是安全的,这就更清楚地表明,同样的value将适用于所有building。
city = 'Beijing'
for building in buildings:
addresses.append(building.street_address, city)
这样还可以提高性能——循环中的任何语句都将在每次循环运行时执行。在这些多次执行上花费的时间就被浪费了,因为它只需要执行一次。如果语句涉及到对数据库的调用或其他耗时的任务,那么这种节省可能是非常重要的。