常见错误1:错误地将表达式作为函数的默认参数
在Python中,我们可以为函数的某个参数设置默认值,使该参数成为可选参数。虽然这是一个很好的语言特性,但是当默认值是可变类型时,也会导致一些令人困惑的情况。我们来看看下面这个Python函数定义:
\>>> def foo(bar=\[\]): # bar是可选参数,如果没有提供bar的值,则默认为\[\],
... bar.append("baz") # 但是稍后我们会看到这行代码会出现问题。
... return bar
Python程序员常犯的一个错误,就是想当然地认为:在每次调用函数时,如果没有为可选参数传入值,那么这个可选参数就会被设置为指定的默认值。在上面的代码中,你们可能觉得重复调用foo()函数应该会一直返回’baz’,因为你们默认每次foo()
函数执行时(没有指定bar
变量的值),bar
变量都被设置为[](也就是,一个新的空列表)。
但是,实际运行结果却是这样的:
\>>> foo()
\["baz"\]
>>> foo()
\["baz", "baz"\]
>>> foo()
\["baz", "baz", "baz"\]
很奇怪吧?为什么每次调用foo()
函数时,都会把"baz"这个默认值添加到已有的列表中,而不是重新创建一个新的空列表呢?
答案就是,可选参数默认值的设置在Python中只会被执行一次,也就是定义该函数的时候。因此,只有当foo()
函数被定义时,bar
参数才会被初始化为默认值(也就是,一个空列表),但是之后每次foo()
函数被调用时,都会继续使用bar
参数原先初始化生成的那个列表。
当然,一个常见的解决办法就是:
\>>> def foo(bar=None):
... if bar is None: # or if not bar:
... bar = \[\]
... bar.append("baz")
... return bar
...
>>> foo()
\["baz"\]
>>> foo()
\["baz"\]
>>> foo()
\["baz"\]
常见问题2:错误地使用类变量
我们来看下面这个例子:
\>>> class A(object):
... x = 1
...
>>> class B(A):
... pass
...
>>> class C(A):
... pass
...
>>> print A.x, B.x, C.x
1 1 1
这个结果很正常。
\>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
嗯,结果和预计的一样。
\>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
在Python语言中,类变量是以字典的形式进行处理的,并且遵循方法解析顺序(Method Resolution Order,MRO)。因此,在上面的代码中,由于类C中并没有x
这个属性,解释器将会查找它的基类(base class,尽管Python支持多重继承,但是在这个例子中,C的基类只有A)。换句话说,C并不没有独立于A、真正属于自己的x
属性。所以,引用C.x
实际上就是引用了A.x
。如果没有处理好这里的关系,就会导致示例中出现的这个问题。
常见错误3:错误地指定异常代码块(exception block)的参数
请看下面这段代码:
\>>> try:
... l = \["a", "b"\]
... int(l\[2\])
... except ValueError, IndexError: # To catch both exceptions, right?
... pass
...
Traceback (most recent call last):
File "", line 3, in
IndexError: list index out of range
这段代码的问题在于,except
语句并不支持以这种方式指定异常。在Python 2.x中,需要使用变量e
将异常绑定至可选的第二个参数中,才能进一步查看异常