如何利用Python的禅理帮助您编写更好的代码
想要在编写Python代码方面取得更好的进步吗?下面就是“Python之禅”如何帮助您朝这个目标迈出第一步。
Python非常简单易学。但是编写符合最佳实践且易于维护的惯用的Python代码可能具有挑战性,特别是对于初学者来说。引入了蒂姆·彼得斯(Tim Peters)的一首诗《Python之禅》的链接,它概述了编写符合最佳实践的Python代码的重要性。
要阅读《Python之禅》,您可以启动Python REPL并运行:
>>> import this
Python之禅,蒂姆·彼得斯
美胜于丑。
显式胜于隐式。
简单胜于复杂。
复杂胜于繁琐。
扁平胜于嵌套。
稀疏胜于稠密。
可读性至关重要。
特殊情况不足以打破规则。
尽管实用性胜过纯粹性。
错误永远不应该悄悄地通过。
除非明确禁止。
面对不确定性,拒绝猜测的诱惑。
我们应该有一种——最好只有一种——显而易见的方法来做到这一点。
尽管这种方式可能一开始并不明显,除非你是荷兰人。
现在比永远好。
尽管永远往往比现在好。
如果实现难以解释,那是个坏主意。
如果实现容易解释,那可能是个好主意。
命名空间是一个伟大的主意——让我们做得更多!
如上所述,《Python之禅》中的大部分警句都是不言自明的。有些格言在解释时应与下一个配对,而有些则与之前的格言相矛盾。尽管如此,《Python之禅》是一篇有趣、引人入胜且实用的阅读材料!
解读《Python之禅》
《Python之禅》最初提出了20个关于Python编程的指导原则。然而,到目前为止只有19个格言。让我们来详细了解一下它们。
美胜于丑。
这个格言强调了编写优雅和符合Python习惯的代码的重要性。
以下代码片段存在代码味道:
def square(num):
squares = []
for i in range(num):
squares.append(i*i)
return squares
该函数:
- 初始化一个空列表
- 在函数内部有一个循环,将元素添加到列表的末尾
- 最后返回一个列表
虽然这个函数功能上是正确的,但它并不符合Python习惯,并且很难维护。
您可以使用生成器来更加优雅地编写它。下面是上述函数的生成器函数等效版本:
def square(num):
for i in range(num):
yield i*i
或者更好的是,您可以使用以下生成器推导式:
num = ...
squares = (i*i for i in range(num))
显式胜于隐式。
在编写代码时,不要让其他开发人员和用户猜测代码的隐含或默认行为。要明确。以通配符导入为例:
from some_module import * # 通配符导入
from some_other_module import *
result = some_function() # 这是从哪里来的?
尽量避免使用通配符导入。因为它不是明确的,并且效率低下。在从其他模块导入函数和类时要具体明确:
from some_module import this_function # 显式导入
result = this_function() # 现在我们知道了。
简单胜于复杂。
这个格言指出我们应该保持代码简单,避免不必要的复杂性。例如:您可能想要将字符串反转,然后您实现了以下递归解决方案:
def reverse_string(my_string):
if my_string == "":
return my_string
else:
return reverse_string(my_string[1:]) + my_string[:1]
尽管这个方法起作用,但是这可能是一个过于复杂的解决方案,因为有更简单和更Pythonic的方法可以完成。
这是使用字符串切片的方法:
>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'
这是使用内置方法和函数的方法:
>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'
复杂比复杂好。
那么这句Python之禅中的下一个格言传达了什么意思?
在Python中,字符串反转是一个非常简单的操作。不过,在实践中,我们可能需要更复杂的逻辑。下面是一个相当简单的例子:
假设您需要连接到数据库:
- 您应该首先解析一个toml配置文件,以获取数据库的配置信息。
- 数据库连接器应该被安装。
- 然后,您可以定义一个连接到数据库的函数,预期连接错误,实现错误处理等等。
- 最后,在连接到数据库后,您可以对其进行查询。
尽管这仍然足够简单,但与字符串反转相比,它需要更复杂的逻辑。但这并不意味着它必须是复杂的。您仍然可以有效地使用内置模块的功能,并组织您的代码,以便其他开发人员可以阅读、理解和贡献。
扁平比嵌套好。
扁平的结构比嵌套的结构更容易解析和理解。在项目中工作时,您可能会尝试通过创建单独的模块来隔离功能。然而,过于细致可能会过度。
话虽如此,您可能经常需要超出扁平结构的范围。但即使需要嵌套,也要尽量减少嵌套。
以下是一个例子:
from db_info.config.actions.parse.parse_config import parse_toml # 太难解析了!
...
from db_config.parse_config import parse_toml # 更好!
...
稀疏比密集好。
如果您刚开始成为开发人员,可能会倾向于过度使用语言的某些功能。List comprehensions,例如,是Pythonic的,但仅当您在需要它们的地方使用时。
看看以下推导:
prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# 输出:[('melons', 40)]
列表推导过于密集和难以解析。在这种情况下,使用具有条件的for循环等效更可读。也就是说,这种推导很难理解。🙂
可读性至关重要。
您应该始终编写可读的代码。以下是提高代码可读性的几种简单方法:
- 使用描述性的变量名
- 为函数和类添加文档字符串
- 在需要时注释代码
- 为函数的参数和返回类型添加类型提示
特殊情况并不足以打破规则。
您应该尽可能遵循语言的规则和推荐的最佳实践。
但这总是可能吗?不,这就是为什么我们有下一个格言的原因。
实用性胜过纯粹。
这是对前一个格言的延续。虽然推荐遵循语言的规则,但在某些情况下,不遵循某些原则是完全可以的。
错误不应该悄无声息地传递。
在Python中,运行时错误非常常见。作为一种良好的实践,您应该始终处理错误,而不是将其保持沉默作为快速修复。
您可以预先设想并实现适当的错误处理-针对不同的错误类型:
try:
# 做一些事情
except ErrorType1:
# 做一些其他事情
except ErrorType2:
# 做其他事情
...
您应该避免使用裸露和通用的异常。Python的较新版本(自Python 3.11起)支持异常链和异常组以执行**link_2**。
除非明确被沉默。
这遵循了前面的格言。如果设计要求或允许将错误保持沉默,那么应该明确地这样做。
例如:当连接到数据库时,您可能会遇到由于无效的配置信息而引发的OperationalError。尝试使用自定义配置进行连接。如果出现OperationalError,则使用默认配置并尝试连接到数据库。
try:
# 使用自定义配置进行连接
except OperationalError:
# 使用默认配置连接
面对不确定性,拒绝猜测。
这句格言在Python之禅中是不言自明的。当有疑问时,不要猜测。而是运行代码并检查输出。然后根据您是否有所需的行为,改进可读性或根据需要修改逻辑。
以以下简单的布尔值元组为例:
>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True
应该有一种——最好只有一种——明显的方法来做到这一点。
为了完成某个任务,应该有一种并且只有一种推荐的Pythonic方式来做到这一点。然而,对于任何问题,我们可以有多种解决方案。
即使在简单的字符串反转示例中,我们看到了递归解决方案、字符串切片和`join()`方法。
这也是一个内部玩笑,由于不一致地使用长破折号。我们通常在无前导和尾随空格的情况下使用长破折号。或者我们在前导和尾随空格都使用它。
所以这是我们可以推断出的。强调应该有一种——而且只有一种——Pythonic方法来做事的格言本身可以以两种以上的方式写成。
虽然第一次可能不明显,除非您是荷兰人。
以轻松的笔调写下,这指的是Python的**link_3**,Python的创造者(荷兰人)。完成特定任务的(最)Pythonic方式只有对Python的创建者来说自然而然。
因此,对于开发人员来说,需要经验和从经验中学习,才能更好地利用该语言的特性。
现在比永远好。
与Python之禅中的其他几句格言一样,这一句也可以有几种不同的解释。
一种解释是,作为开发人员,很常见的是拖延开始编写项目的代码。与其等待计划项目的最细节,现在开始是一个更好的主意。
另一种可能的解释是:运行在有限步骤中并终止的代码通常比错误且陷入无限循环的代码要好。
虽然永远比现在好。
这句格言似乎与前一句相矛盾。虽然不拖延是更好的选择,但我们仍然应该仔细思考问题并相应地设计代码。
编写一个模块——而没有经过深思熟虑——充满了代码异味和反模式,这是一个坏主意。因为这样的代码很难重构和实施纠正措施。
如果实现难以解释,那是个坏主意。
任何逻辑——无论多么复杂——都可以以简单易懂的形式实现。
如果实现难以解释,那么可能存在一些不必要的复杂性。可以修改或重构代码,使其更容易理解。
如果实现容易解释,那可能是个好主意。
这与前面的格言相关,并且也是不言自明的。如果可以用简单的术语解释实施,那么它很可能是个好主意。
因为这样的代码,其实现可以用简单的术语描述,很可能是可读性强、易于理解的,且具有最小的复杂性。
命名空间是一个非常好的主意——我们需要更多这样的东西!
在Python中,特定作用域中的对象可以使用其命名空间中的名称访问。例如,您可以创建一个类并使用它作为创建类的实例的模板。现在,实例的实例变量都在实例的命名空间中。
这使得我们可以使用具有相同名称的对象——而无需冲突,因为它们位于不同的命名空间中。但是,只有在需要时才应使用它们,并确保代码的简洁性和可读性不受损。
结论
本教程到此结束!我希望这篇指南帮助您了解Python之禅如何强调Python中的代码风格和良好的编码实践。您编写的代码越多,您将越擅长它。
如果您有兴趣学习如何编写简洁易读的代码,请阅读此文章:Python one-liners。