当前位置: 首页 > 科技观察

八个重构技巧让Python代码更Pythonic

时间:2023-03-13 12:16:14 科技观察

1.MergeAppendtoListDeclaration我们从一个简单的开始。不是声明一个空列表然后附加到它,而是直接用所有元素初始化列表。这缩短了代码并使意图更加明确。它的性能也稍好一些,因为它避免了对append()的函数调用。players=[]players.append("Patrick")players.append("Max")players.append("Jessi")#->refactorplayers=["Patrick","Max","Jessi"]这也适用于填充其他集合类型,例如集合和字典。2使用items()直接解包字典值当遍历字典时,既需要键也需要值,那就不要手动访问值了。而是迭代dictionary.items(),它为您提供键和值。这节省了我们过去分配给玩家的行,代码现在读起来更自然,重复更少。teams_by_color={"blue":["Patrick","Jessi"]}forteam_colorinteams_by_color:players=teams_by_color[team_color]ifis_winning(team_color):advance_level(players)#->refactorforteam_color,playersinteams_by_color.items():ifis_winning(team_color):advance_level(players)3.用枚举替换range(len))。这会将当前索引和当前项作为元组返回。所以我们这里可以直接看值,也可以通过索引访问item。foriinrange(len(players)):print(i,players[i])#->refactorfori,playerinenumerate(players):print(i,player)Enumerate还有一个可选的开始参数。如果您使用它,计数器将从该值开始。但是请注意,这些项目仍然从第一个开始。fori,playerinenumerate(players,start=1):print(i,player)4.将手动循环计数器替换为枚举调用这与之前非常相似。有时我看到直接对项目执行迭代的代码——这本身并不坏——但随后需要一个在循环内手动递增的计数器。同样在这里,您可以简单地使用枚举函数。这更简单,更快。i=0forplayerinplayers:print(i,player)i+=1#->refactorfori,playerinenumerate(players):print(i,player)4.1如果只需要计数,不要手动更新计数器项目数,也不要遍历循环并手动计算所有项目。相反,只需使用len()函数来获取列表中的元素数。num_players=0forplayerinplayers:num_players+=1#->refactornum_players=len(players)5.将条件简化为一个return语句当我们到达一个方法的末尾,想要返回True或False时常见的它的工作方式是这样的。如果条件为真,我们返回真。否则我们最后返回False。不过直接返回结果更简洁:deffunction():ifisinstance(a,b)orissubclass(b,a):returnTruereturnFalse#->refactordeffunction():returnisinstance(a,b)或issubclass(b,a)我们在这里应该注意的一件事是,只有当表达式的计算结果为布尔值时才能这样做。isinstance()和issubclass()都是返回布尔值的函数,所以这很好。但在下一个示例中,第一个表达式pythonistas是一个列表而不是布尔值。如果pythonistas是一个有效的非空列表,这将返回列表而不是预期的布尔值,那么它可能是您的应用程序中的错误。因此,为了确保我们在这里返回一个布尔值,我们可以将返回包装在对bool()函数的调用中。defany_pythonistas():pythonistas=[编码器中编码器的编码器ifis_good_in_python(coder)](coder)]returnbool(pythonistasorself.is_pythonista())6.合并条件中的重复块我们应该始终寻找机会删除重复代码。如果在if...elif链中有多个相同的块,那么执行此操作的一个好地方。在此示例中,if和elif都会导致执行相同的函数。所以我们可以使用or组合前两个块来删除对函数的重复调用。现在,如果我们需要更改process_standard_payment()行,我们可以在一处而不是两处进行。defprocess_payment(payment,currency):ifcurrency=="USD":process_standard_payment(payment)elifcurrency=="EUR":process_standard_payment(payment)else:process_international_payment(payment)#->refactordefprocess_payment(payment,currency):ifcurrency=="USD"orcurrency=="EUR":process_standard_payment(payment)else:process_international_payment(payment)7.用in运算符替换同一个变量的多次比较我们甚至可以进一步重构之前的代码。由于我们针对多个值重复检查同一个变量,我们可以使用in运算符来缩短它。如果货币值在定义的列表中,我们将执行专门的操作。defprocess_payment(payment,currency):如果currency=="USD"orcurrency=="EUR":process_standard_payment(payment)else:process_international_payment(payment)#->refactordefprocess_payment(payment,currency):ifcurrencyin["USD","EUR"]:process_standard_payment(payment)else:process_international_payment(payment)为了再次改进这一点,我们应该在这里使用一个集合。在集合中查找值会更快,而且我们这里无论如何都想要唯一的元素,所以集合是更好的选择。#->refactordefprocess_payment(payment,currency):ifcurrencyin{"USD","EUR"}:process_standard_payment(payment)else:process_international_payment(payment)8.如果你已经是,用yieldfrom替换for循环中的yieldfrom熟悉generator,那么这就是进阶trick了。一个经常被忽视的小技巧是,Python的yield关键字对于可迭代对象有对应的yieldfrom。如果你有一个像列表这样的可迭代对象,而不是说foriteminiterable:yielditem,你可以简单地说yieldfromiterable。这更短并且消除了对可迭代对象的手动循环,这也可以提高性能。defget_content(entry):forblockinentry.get_blocks():yieldblock#->refactordefget_content(entry):yieldfromentry.get_blocks()复制代码