常见错误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.x1 1 1
这个结果很正常。
>>> B.x = 2>>> print A.x, B.x, C.x1 2 1
嗯,结果和预计的一样。
>>> A.x = 3>>> print A.x, B.x, C.x3 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, inIndexError: list index out of range
这段代码的问题在于,except语句并不支持以这种方式指定异常。
在Python 2.x中,需要使用变量e将异常绑定至可选的第二个参数中,才能进一步查看异常的情况。
因此,在上述代码中,except语句并没有捕获IndexError异常;
而是将出现的异常绑定到了一个名为IndexError的参数中。
要想在except语句中正确地捕获多个异常,则应将第一个参数指定为元组,然后在元组中写下希望捕获的异常类型。
另外,为了提高可移植性,请使用as关键词,Python 2和Python 3均支持这种用法。
>>> try:... l = ["a", "b"]... int(l[2])... except (ValueError, IndexError) as e:... pass...>>>
- END -
往期精彩◆ 50款开源工具你都用过吗?
◆ 过于真实!程序员忙碌的真相
◆ python网络爬虫的基本原理详解