PEP 8 – Python 代码风格指南中文版(六)

编程建议(1)

  • 我们应该以一种不会对其他Python实现(比如PyPy、Jython、IronPython、Cython、Psyco等)造成不利影响的方式来编写。

例如,不要依赖CPython中对于a += b或a = a + b形式的语句在原地字符串连接上的高效实现。这种优化即使在CPython中也是脆弱的(它仅对某些类型有效),并且在不使用引用计数的实现中根本不存在。在库的性能敏感部分,应使用''.join()形式来确保跨各种实现的连接操作是线性的。

解释:

这里的其他Python实现指的是除了CPython(最广泛使用的Python解释器)之外的其他Python解释器或编译器。每个实现都有其独特的特点和优势,比如PyPy专注于提高Python代码的执行速度,Jython可以让Python代码在Java虚拟机上运行,IronPython可以让Python代码在.NET框架上运行等。

换句话说,我们应该让我们的代码尽可能具有可移植性和兼容性,以便它可以在不同的Python解释器或编译器上运行,而不会出现性能下降、功能缺失或其他问题。

  • 当你需要比较一个变量是否等于None这样的单例时,应该总是使用isis not操作符,而不是使用等号(==)或不等号(!=)这样的相等性操作符。

另外,当你实际上想要表达if x is not None时,要小心不要直接写if x——比如,在测试一个默认为None的变量或参数是否被设置为其他值时。那个“其他值”可能有一个在布尔上下文中会被视为False的类型(比如一个容器)!

解释:

在Python中,None是一个特殊的单例对象,意味着整个程序中只有一个None。因此,当你想要检查一个变量是否确实指向了None(而不是仅仅值上相等),你应该使用isis not来比较。这是因为is操作符比较的是两个对象的身份(即它们是否是同一个对象),而==操作符比较的是两个对象的值。虽然对于None这样的单例来说,使用==is在结果上可能是相同的,但使用is是更加准确和推荐的方式。

在Python中,if语句后面的表达式会被隐式地转换成布尔值。很多值在布尔上下文中都会被视为False,除了True、任何非零数值、非空字符串、非空容器(如列表、字典、集合等)以及非None的对象。因此,如果你只是简单地写if x来检查一个变量x是否“为真”,那么当x是一个空容器或其他在布尔上下文中被视为False的值时,这个检查就会失败。而如果你实际上想要检查的是x是否不是None,那么你应该明确地写if x is not None。这样,无论x是什么类型的值,只要它不是None,这个条件就会为真。

  • 使用is not操作符而不是not ... is。虽然这两个表达式在功能上是相同的,但前者更易于阅读,是首选的写法。

# 正确的:
if foo is not None:

# 错误的:
if not foo is None:

 解释:

在Python中,当你想要检查一个对象是否不是某个特定的单例(比如None)时,你有两种方式来写这个逻辑:

  1. 使用not ... is的方式,比如not x is None
  2. 使用is not的方式,即x is not None

虽然从逻辑上讲,这两种写法都是正确的,并且它们会得到相同的结果,但是PEP-8建议我们使用第二种方式(is not),因为它更加直观和易于阅读。

原因在于,当我们按照英语的阅读习惯从左到右扫描代码时,x is not None更符合我们的思维习惯。它首先识别出x,然后紧接着是一个清晰的否定判断is not,最后是一个明确的比较对象None。这种结构让我们能够快速地理解代码的含义,而不需要在脑海中重新组织语句的顺序。

相比之下,not x is None虽然逻辑上也是正确的,但它需要我们先处理x is None这个子表达式,然后再对结果取反。这种额外的处理步骤可能会让代码的阅读者稍微感到困惑,尤其是在更复杂的表达式中。

因此,遵循PEP-8的建议,我们应该优先使用is not来编写这样的逻辑表达式。

  • 在实现带有丰富比较功能的排序操作时,最好实现所有六个比较操作(__eq__, __ne__, __lt__, __le__, __gt__, __ge__),而不是依赖其他代码仅执行特定的比较。

为了最小化所需的工作量,functools.total_ordering() 装饰器提供了一个工具来生成缺失的比较方法。

PEP 207 指出 Python 假定反身性规则。因此,解释器可能会将 y > x 替换为 x < y,将 y >= x 替换为 x <= y,并可能交换 x == y 和 x != y 的参数。sort() 和 min() 操作保证使用 < 操作符,而 max() 函数使用 > 操作符。然而,最好实现所有六个操作,以避免在其他上下文中产生混淆。

解释:

在Python中,如果你想让你的对象支持排序和比较操作,那么最好实现所有六种比较方法(相等、不等、小于、小于等于、大于、大于等于)。不要只实现其中一部分,然后期望其他代码来填补剩下的空白。

不过,为了让你不那么辛苦,Python的functools模块提供了一个total_ordering()装饰器。这个装饰器可以帮你自动生成缺失的比较方法,只要你实现了__eq__和至少一个“不严格”的比较方法(比如__lt____gt__),它就能利用这些方法来推断出其他缺失的比较方法。

Python有一个规则,叫做“反射性规则”,它允许解释器在比较操作中进行一些优化,比如交换比较运算符的两边。这意味着,即使你只实现了部分比较方法,Python也可能会在内部使用它们来进行其他类型的比较。但是,为了确保你的对象在所有情况下都能正确地进行比较,最好还是实现所有六种比较方法。特别是,当你使用sort()min()max()等函数时,虽然它们有明确的操作符偏好,但在其他自定义的上下文中,完整的比较实现将避免潜在的混淆和错误。

总之,当你想要让你的Python对象支持排序和比较时,遵循PEP-8的建议,实现所有六种比较方法是一个好习惯。虽然functools.total_ordering()装饰器可以帮你减轻一些负担,但理解这些比较方法如何工作以及为什么需要它们仍然是很重要的。

  • 始终使用 def 语句而不是将 lambda 表达式直接绑定到标识符的赋值语句:

这意味着,当你需要定义一个函数时,应该使用 def 关键字来明确声明它,而不是通过赋值语句将 lambda 表达式的结果(即一个函数对象)赋给一个变量名。这样做可以提高代码的可读性和可维护性。

# 正确的:
def f(x): return 2*x

# 错误的:
f = lambda x: 2*x

第一种形式意味着生成的函数对象的名称具体是 'f' 而不是通用的 '<lambda>'。这在回溯和一般的字符串表示中更为有用。使用赋值语句消除了 lambda 表达式相对于显式 def 语句所能提供的唯一好处(即它可以嵌入到更大的表达式中)。

解释:

在Python中,你可以使用两种方式来定义函数:一种是使用def关键字,后跟函数名和参数列表,以及函数体;另一种是使用lambda关键字来创建一个匿名函数(即没有名称的函数),并将其赋值给一个变量。然而,PEP-8建议尽量使用def语句来定义函数,因为它更加清晰和易于维护。

lambda 表达式用于创建匿名函数(即没有名称的函数)。然而,当你将 lambda 表达式的结果(即函数对象)赋值给一个变量时,虽然这个变量现在引用了这个函数对象,但函数对象本身仍然保持匿名(在内部表示中,它可能仍然显示为 '<lambda>')。相比之下,使用 def 语句定义的函数,你可以给函数指定一个有意义的名称,这个名称会在错误追踪、调试和文档等场合中使用。而lambda表达式创建的匿名函数在错误追踪中只会显示为<lambda>,这可能会让调试变得更加困难。此外,在字符串表示中,使用明确的函数名也更易于理解。。

虽然 lambda 表达式允许你将函数定义嵌入到更大的表达式中,但如果你只是将 lambda 表达式的结果赋值给一个变量(如 f = lambda x: x + 1),那么你就失去了 lambda 表达式的这一主要优势,因为此时你已经通过变量名 f 显式地引用了这个函数。在这种情况下,使用 def 语句会更清晰、更易于维护。

 相关文章:

PEP 8 – Python 代码风格指南中文版(一)

PEP 8 – Python 代码风格指南中文版(二)

PEP 8 – Python 代码风格指南中文版(三)

PEP 8 – Python 代码风格指南中文版(四)

PEP 8 – Python 代码风格指南中文版(五)

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值