Python 陷阱
1. 可变的默认参数
错误示例
>>> def lala(l=[]): ... l.append(123) ... print l ... >>> lala() [123] >>> lala() [123, 123] >>> lala() [123, 123, 123]
原因是,一个函数的默认值,仅在函数被定义时赋值一次。
解决方法
>>> def lala(l=None): ... if l is None: ... l = [] ... l.append(123) ... print l ... >>> lala() [123] >>> lala() [123] >>> lala([456]) [456, 123]
2. 闭包问题
问题1:期望得到一个每执行一次,数字加1的函数
错误示例
>>> def counter(): ... count = 0 ... def func(): ... count += 1 ... print count ... return func ... >>> lala = counter() >>> lala() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in func UnboundLocalError: local variable 'count' referenced before assignment
错误的原因是,在python中,当试图为一个变量赋值时,会认为该变量在local(见《命名空间与作用域》)
正确的做法
>>> def counter(): ... count = [0] ... def func(): ... count[0] += 1 ... print count[0] ... return func ... >>> lala = counter() >>> lala() 1 >>> lala() 2
因为在list是可变对象,因此可以巧妙的跳过这个限制。
3. for 循环相关
1. 循环的同时修改列表
目的:传入一个纯数字的列表,将每个数字的平方添加在原列表中
错误的做法:
>>> def lala(l): ... for i in l: ... l.append(i**2) ... >>> lala([1,2,3]) #死循环
正确做法: 用list[:]获得一个拷贝
>>> def lala(l): ... for i in l[:]: ... l.append(i**2) ... return l ... >>> lala([1,2,3]) [1, 2, 3, 1, 4, 9]
2. for循环不会引入新的命名空间
>>> for i in [1,2,3]: ... print i ... 1 2 3 >>> i 3
感觉i应该是循环内部使用的一个变量,但是却发现循环结束后,它"泄漏"到外部了,这个没有解决办法,自己注意一下吧
3. for循环闭包问题
目的:期望得到5个函数,分别是将一个数字与0,1,2,3,4相乘
错误做法:
>>> def create(): ... return [lambda x: x * i for i in range(5)] ... >>> for i in create(): ... i(2) ... 8 8 8 8 8
这是后期绑定(late binding)导致的,这是指内层函数被执行时,才会去查找该变量的值。请注意上面讲的,for循环会导致变量泄漏,所以当函数执行的时候,i已经等于4了。
上面的代码等价于:
>>> def create(): ... result = [] ... for i in range(5): ... def lala(x): ... return x * i ... result.append(lala) ... return result
再理解一次,内层函数lala在执行时,才会去找i是多少,而执行的时候i已经等于4了。
解决方法:
>>> def create(): ... return [lambda x, y=i: x * y for i in range(5)] ... >>> for i in create(): ... i(2) ... 0 2 4 6 8
利用前面所讲的,带默认函数的函数将会在函数定义时完成赋值,来解决这个问题
未完成