普通的Gotchas¶

在很大程度上,Python的目标是成为一种干净一致的语言,避免意外。然而,有几个案例可能会让新人感到困惑。
其中一些案例是故意的,但可能会让人大吃一惊。有些可以说是语言疣。一般说来,下面是一系列潜在的棘手行为,乍一看可能很奇怪,但一旦你意识到了惊喜的潜在原因,这些行为通常都是明智的。
可变默认参数¶
似乎是 最 新的Python程序员遇到的常见意外是Python在函数定义中处理可变的默认参数。
你写的东西¶
def append_to(element, to=[]):
to.append(element)
return to
你可能期望会发生什么¶
my_list = append_to(12)
print(my_list)
my_other_list = append_to(42)
print(my_other_list)
如果没有提供第二个参数,则每次调用函数时都会创建一个新列表,因此输出为:
[12]
[42]
到底发生了什么¶
[12]
[12, 42]
创建新列表 once 当函数被定义,并且在每个连续调用中使用相同的列表时。
计算Python的默认参数 once 当函数被定义时,不是每次调用函数时(比如Ruby中的函数)。这意味着,如果使用可变的默认参数并对其进行可变,那么 will 并为将来对函数的所有调用改变了该对象。
你应该怎么做¶
每次调用函数时,通过使用默认参数来表示没有提供参数,创建一个新对象。 (None
通常是个不错的选择)。
def append_to(element, to=None):
if to is None:
to = []
to.append(element)
return to
别忘了,你通过了 list 对象作为第二个参数。
当Gotcha不是Gotcha时¶
有时您可以专门“利用”(读:按预期使用)此行为来维护函数调用之间的状态。这通常在编写缓存函数时完成。
后期装订闭合¶
另一个常见的混淆源是Python在闭包中(或在周围的全局范围内)绑定变量的方式。
你写的东西¶
def create_multipliers():
return [lambda x : i * x for i in range(5)]
你可能期望会发生什么¶
for multiplier in create_multipliers():
print(multiplier(2))
包含五个函数的列表,每个函数都有自己的结束符 i
使其参数相乘的变量,产生:
0
2
4
6
8
到底发生了什么¶
8
8
8
8
8
创建了五个函数,而不是全部都是乘法 x
到4点。
Python的闭包是 后期装订 。这意味着在调用内部函数时会查找闭包中使用的变量值。
在这里,无论何时 any 在返回的函数中,调用 i
在调用时在周围范围中查找。到那时,循环已经完成,并且 i
其最终值为4。
这件事最让人讨厌的是表面上普遍存在的错误信息,这些错误信息与此有关。 lambdas 在 Python 中。使用创建的函数 lambda
表达并不是特别的,事实上,同样的精确行为只是通过使用一个普通的 def
:
def create_multipliers():
multipliers = []
for i in range(5):
def multiplier(x):
return i * x
multipliers.append(multiplier)
return multipliers
你应该怎么做¶
最普遍的解决方案可以说是有点黑客。由于python在评估函数的默认参数方面的上述行为(请参见 可变默认参数 ,您可以创建一个闭包,通过使用如下的默认参数立即绑定到它的参数:
def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]
或者,可以使用functools.partial函数:
from functools import partial
from operator import mul
def create_multipliers():
return [partial(mul, i) for i in range(5)]
当Gotcha不是Gotcha时¶
有时您希望闭包以这种方式运行。后期装订在很多情况下都很好。不幸的是,创建独特函数的循环会导致打嗝。
字节码(.pyc)文件无处不在!¶
默认情况下,当从文件执行python代码时,Python解释器将自动将该文件的字节码版本写入磁盘,例如。 module.pyc
。
这些 .pyc
不应将文件签入源代码存储库。
理论上,出于性能原因,此行为在默认情况下处于启用状态。如果没有这些字节码文件,Python将在每次加载文件时重新生成字节码。
禁用字节码(.pyc)文件¶
幸运的是,生成字节码的过程非常快,并且在开发代码时不需要担心。
那些文件很烦人,我们把它们扔掉吧!
$ export PYTHONDONTWRITEBYTECODE=1
与 $PYTHONDONTWRITEBYTECODE
设置环境变量后,python将不再将这些文件写入磁盘,您的开发环境将保持良好和干净。
我建议在 ~/.profile
。
删除字节码(.pyc)文件¶
以下是删除所有这些文件的好技巧,如果它们已经存在:
$ find . -type f -name "*.py[co]" -delete -or -type d -name "__pycache__" -delete
从项目的根目录运行它,以及 .pyc
文件会突然消失。好多了。
版本控制忽略¶
如果你还需要 .pyc
由于性能原因,可以将文件添加到版本控制存储库的忽略文件中。流行的版本控制系统能够使用文件中定义的通配符来应用特殊规则。
忽略文件将确保匹配的文件不会签入存储库。 Git 使用 .gitignore
虽然 Mercurial 使用 .hgignore
。
至少您的忽略文件应该如下所示。
syntax:glob # This line is not needed for .gitignore files.
*.py[cod] # Will match .pyc, .pyo and .pyd files.
__pycache__/ # Exclude the whole folder
您可能希望根据需要包含更多的文件和目录。下次提交到存储库时,这些文件将不包括在内。