错误1、函数的默认参数使用不当 Python允许我们为参数指定一个默认参数值,这是很好的特性。但是当默认参数是mutable类型时,就容易忽略下面的问题: >>> def foo(bar=[]): # bar是一个默认参数,如果没有指定值,值为[] ... bar.append("abc") # 这一行也许会带来问题 ... return bar 这里一个常犯的错误是,想当然的认为使用默认参数调用foo函数时,bar的默认值总为[]。例如我们重复调用foo()(不指定参数),期望它每次返回的都是‘abc’。这是错的,实际上返回如下结果: >>> foo() ['abc'] >>> foo() ['abc', 'abc'] >>> foo() ['abc', 'abc', 'abc'] 这是因为这个默认参数只创建一次,不管你调用多少次这个函数。 可以修改为如下代码: >>> def foo(bar=None): # 改为非mutable类型 ... if bar is None: ... bar = [] ... bar.append("abc") ... return bar ... >>> foo() ['abc'] >>> foo() ['abc'] >>> foo() ['abc']
错误2、类成员变量的使用 看下面一个例子: >>> class A(object): ... num = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.num, B.num, C.num 1 1 1 好,没什么问题。 >>> B.num = 2 >>> print A.num, B.num, C.num 1 2 1 和预想的一样 >>> A.num = 3 >>> print A.num, B.num, C.num 3 2 3 这是怎么回事,我们只改变了A.num,为什么C.num也改了。 在Python中,类成员变量在内部是以字典形式处理的。在上面代码中,因为num在C中没有找到,它会查找它的基类。(和B不同)
错误3、异常处理参数不正确 >>> try: ... abc=['a', 'b'] ... int(abc[2]) ... except ValueError, IndexError: # 试图抓取两个异常 ... pass ... Traceback (most recent call last): # 只抓到一个异常,第二个异常没有抓到 File "<stdin>", line 3, in <module> IndexError: list index out of range 上面问题在于except语句不接受list。正确的抓取多个异常应使用元组,为了兼容Python2和Python3,我们使用as关键字,如下: >>> try: ... abc=['a', 'b'] ... int(abc[2]) ... except (ValueError, IndexError) as e: ... pass ... >>>
错误4、错误理解Python作用域规则 Python作用域基于LEGB规则,代表Local, Enclosing,Global, Built-in。但是在Python中会有细微的不同,看如下代码: >>> a = 10 >>> def foo(): ... a += 1 # 同 a = a + 1 ... print a ... >>> foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo UnboundLocalError: local variable 'a' referenced before assignment 上面问题的出现是因为在函数中对变量进行了(+=)赋值,Python会自动认为a是一个局部变量并屏蔽了外部变量。如果把上面代码改为a = 1,就没有问题,。下面看一个list的例子: >>> l = [1, 2, 3] >>> def foo1(): ... l.append(5) # 这里没有问题,它使用的是全局变量l ... >>> foo1() >>> l [1, 2, 3, 5] >>> >>> >>> l = [1, 2, 3] >>> def foo2(): ... l += [5] # 这里有问题,l被认为是局部变量(同l = l + [5]) ... >>> foo2() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo2 UnboundLocalError: local variable 'l' referenced before assignment
错误5、在遍历list的同时删除其中的元素 看下面代码: >>> is_odd = lambda x : bool(x % 2) >>> nums = [i for i in range(10)] >>> nums [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> for i in range(len(nums)): ... if is_odd(nums[i]): ... del nums[i] ... Traceback (most recent call last): File "<stdin>", line 2, in <module> IndexError: list index out of range 在list或数组中边遍历边删除元素,在其他编程语言中也会出现问题。幸运的是在Python中提供了list comprehensions,实现上面功能,代码修改如下: >>> is_odd = lambda x : bool(x % 2) >>> nums = [i for i in range(10)] >>> nums [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> nums[:] = [n for n in nums if not is_odd(n)] >>> nums [0, 2, 4, 6, 8]
错误6、Python的变量绑定(closure) 考虑如下代码: >>> def create_mul(): # 生成匿名函数 ... return [lambda x : i * x for i in range(5)] ... >>> for m in create_mul(): ... print m(3) 希望的输出为: 0 3 6 9 12 实际的输出为: 12 12 12 12 12 出现上面问题是因为Python的延迟绑定行为,不管什么时候调用函数,i的值都是4。 解决方法如下: >>> def create_mul(): ... return [lambda x, i = i : i * x for i in range(5)] ... >>> for m in create_mul(): ... print m(3) ... 0 3 6 9 12 上面代码有点怪,这里用的是匿名函数的默认参数。
错误7、模块的循环依赖 假设我们有两个文件a.py和b.py,它们之间相互包含: import b def foo(): return b.x print foo() b.py: import a x = 1 def bar(): print a.foo() 我们先来导入a再来导入b: >>> import a 1 >>> import b >>> 这里没有问题,如果我们单独导入b: >> import b Traceback (most recent call last): File "<stdin>", line 1, in <module> File "b.py", line 1, in <module> import a File "a.py", line 6, in <module> print foo() File "a.py", line 4, in foo return b.x AttributeError: 'module' object has no attribute 'x' |