Python 之禅如何帮助您编写更好的代码

提升Python代码质量:Python之禅的指导意义

您是否渴望编写更优秀的Python代码?Python之禅是您迈向这一目标的重要一步。

Python语言易于上手,但要编写出易于维护、符合Python惯例的代码,对初学者而言可能具有挑战性。 PEP-20 引入了蒂姆·彼得斯(Tim Peters)的诗作《Python之禅》,它概述了遵循最佳实践编写Python代码的重要性。

要阅读《Python之禅》,只需启动Python REPL并运行以下命令:

>>> import this
The Zen of Python, by Tim Peters

优美胜于丑陋。
显式胜于隐式。
简单胜于复杂。
复杂胜于繁琐。
扁平胜于嵌套。
稀疏胜于密集。
可读性至关重要。
特殊情况并不特殊到足以打破规则。
尽管实用性胜过纯粹性。
错误绝不应该悄无声息地传递。
除非明确地使其静默。
面对模棱两可,拒绝猜测的诱惑。
应该有一种——最好只有一种——明显的做法。
尽管那种做法一开始可能并不明显,除非你是荷兰人。
现在做总比不做好。
尽管不做好总比*立即*做好要好。
如果实现很难解释,那一定是个坏主意。
如果实现容易解释,那很可能是一个好主意。
命名空间是一个绝妙的主意——让我们多用它们!

如您所见,《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]

虽然递归可以实现目标,但对于这个简单的任务来说有些过度设计。 因为有更简单、更符合Python习惯的方法可以实现它。

这是使用字符串切片的方法:

>>> 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 # 好多了!
...

稀疏胜于密集。

在开发初期,你可能会过度使用语言的某些特性。 例如,列表推导式很符合Python风格,但只有在必要时才应该使用。

以下面的列表推导式为例:

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:
    # 处理ErrorType1
except ErrorType2:
    # 处理ErrorType2
...

您应该避免使用泛泛的异常处理。较新版本的Python(从Python 3.11起)支持异常链和异常组,以实现更复杂的异常处理。

除非明确地使其静默。

这是对前一条格言的补充。如果设计需要或允许忽略错误,那么应该明确地执行。

例如,连接数据库时,可能会因为配置信息无效而遇到OperationalError。 尝试使用自定义配置连接。 如果出现操作错误,则使用默认配置尝试连接数据库。

try:
   # 使用自定义配置连接
except OperationalError:
   # 使用默认配置连接

面对模棱两可,拒绝猜测的诱惑。

Python之禅中的这条格言是不言自明的。 如果有疑问,不要猜测。 运行代码并检查输出。 然后根据您是否得到预期的结果,提高代码可读性或根据需要修改逻辑。

考虑以下使用布尔元组的简单例子:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

应该有一种——最好只有一种——明显的做法。

对于完成某项任务,应该有一个且只有一种推荐的、符合Python习惯的方法。 然而,对于任何问题,我们都可以找到多种解决方案。

即使在简单的字符串反转示例中,我们也探讨了递归、字符串切片和join()方法。

这其中也包含一个“梗”,因为连字符的使用并不一致。 我们通常使用长破折号,不带前导和尾随空格。或者,我们使用带前导和尾随空格的长破折号。

这就是我们可以推断出来的:强调应该有一种(且只有一种)符合Python风格的方法的格言,本身可以用两种以上的方式来表达。

尽管这种方式一开始可能并不明显,除非你是荷兰人。

简单来说,这是指Python的创建者吉多·范·罗苏姆(Guido Van Rossum),他是荷兰人。 (最)符合Python习惯的方式——自然只有Python的创建者才能立即想到。

因此,开发者需要经验和学习,才能更好地利用该语言的功能。

现在总比不做好。

就像Python之禅中的其他几条格言一样,这句话也可以有多种不同的解读方式。

一种解释是,作为开发人员,拖延开始编写项目代码是很常见的。与其等待将项目规划到最细微的细节,不如现在就开始行动。

另一种可能的解释是:以有限的步骤运行并终止的代码,通常优于有错误并陷入无限循环的代码。

尽管不做好总比*立即*做好要好。

这条格言似乎与前一条格言相矛盾。虽然最好不要拖延,但我们仍然需要思考问题并相应地设计代码。

在没有经过深思熟虑的情况下编写充满代码坏味道和反模式的模块是一个糟糕的主意。因为这样的代码很难重构和修复。

如果实现很难解释,那一定是个坏主意。

任何逻辑,无论多么复杂,都可以用易于解释和理解的形式实现。

如果实现很难解释,那么可能存在不必要的复杂性。可以修改或重构代码,以便更容易理解。

如果实现容易解释,那很可能是一个好主意。

这条格言与前一条相关,而且是不言自明的。 如果可以用简单的术语解释实现,那么这可能是一个好主意。

因为这样的代码,其实现可以用简单的术语来描述,很可能是可读且易于遵循的,并且复杂性最小。

命名空间是一个绝妙的主意——让我们多用它们!

在Python中,可以使用命名空间中的名称来访问特定范围内的对象。例如,您可以创建一个类,并将其用作模板来创建该类的实例。现在,实例变量将位于该实例的命名空间中。

这使得我们可以使用同名对象,而不会发生冲突,因为它们位于不同的命名空间。 但是,应该根据需要使用它们,并确保代码的简洁性和可读性不受影响。

总结

这就是本教程的全部内容! 我希望本指南能够帮助您了解Python之禅是如何强调Python中的代码风格和良好编码实践的。 你编写的代码越多,就会越优秀。

如果您有兴趣学习如何编写简洁易读的代码,请阅读这篇关于Python单行代码的文章。