原文:http://docs.python-guide.org/en/latest/writing/gotchas/
大多数情况下,Python旨在写出干净,一致的代码,避免奇怪的做法。但是,对新手来说有时却不是这样。
可变的(Mutable)默认参数
可能你写的代码像这样:
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
如果没有提供第二个参数,每次调用函数会返回一个新的list,所以输出:
[12]
[42]
实际情况可能让你大失所望:
[12]
[12, 42]
新的list只会被创建一次,接下来的多次调用会使用相同的list。
也就是说,你如果使用了一个可变类型的默认参数并且调用的时候修改了它,那么将来的每次调用你都使用的修改之后的对象。
怎样避免这种情况呢
最佳实践:使用一个默认的参数标识没有提供参数的情况(None通常是一个好的选择),每次调用函数时显示地创建一个新的对象。
def append_to(element, to=None):
if to is None:
to = []
to.append(element)
return to
有时候你可能是有意为之,例如写缓存函数的时候。
晚绑定闭包
另外一个陷阱是Python在闭包中绑定值的时候。
你可能会写这样的代码:
def create_multipliers():
return [lambda x : i * x for i in range(5)]
你期望这样调用:
for multiplier in create_multipliers():
print multiplier(2)
期望产生的输出:
0
2
4
6
8
事实却并非所愿:
8
8
8
8
8
创建了5个函数,所有的函数都是乘以4.
Python的闭包采用的是晚绑定,即是说,闭包中使用的值会在内部函数调用的时候去查找。
上面的情况中不管函数什么时候被调用,变量i的值都会使用最终的值,也就是4.
在此处使用lambda表达式可能会令人误解,如果换成一个嵌套的函数定义可能你会更清楚一些:
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)]
某些情况下,你可能希望你的闭包按照这种方式工作。晚绑定在很多情况下都是有益的,在循环中创建唯一的函数是一个特殊的例子。
注:笔者根据自己的理解使用的意译,翻译的不好还请多多指教