Python 中的常见陷阱

原文: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)]
某些情况下,你可能希望你的闭包按照这种方式工作。晚绑定在很多情况下都是有益的,在循环中创建唯一的函数是一个特殊的例子。


注:笔者根据自己的理解使用的意译,翻译的不好还请多多指教


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值