Python编程中容易出现的10个错误

Python是一个解释型、面向对象、具有动态语义的高级语言。它具有高级数据结构、动态类型绑定,支持模块化。由于Python程序员往往并非只会这一门语言,受其他语言的影响,我们在编写Python代码的时候容易出现一些错误,本文中列出了10点。

一、函数参数默认值
Python允许定义函数参数的默认值,这和C/C++是一致的。但是所不同的是,解释器只对该默认参数赋值一次。

>>> def foo(bar=[]):
...    bar.append("baz”)    
...    return bar

比如说这个foo函数,如果调用的时候自带有参数foo(a),这没什么可说的。如果用foo(),那么bar只有在一次调用无参函数foo()的时候才会执行bar=[],之后若再调用foo(),那么bar会继续使用刚才的bar,这个bar就类似于C中的static局部变量。

>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz”]


解决办法是,在函数内部做判断。如果默认参数想定义为空的类型,用None。

>>> def foo(bar=None):
...    if bar is None:# or if not bar:
...        bar = []
...    bar.append("baz")
...    return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz”]


二、类变量的继承
Python里,类变量的内部是由字典实现的。在派生类变量中,如果成员没有被定义,则会去基类中找。如果有的话的就不去看基类的了。

>>> 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

三、异常处理时参数声明
异常处理时except后面不能跟多个异常。
>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError: 
...     pass

上面这么写是不对的,第二个异常情况是捕获不到的,应该像下面这样写。

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except (ValueError, IndexError) as e:  
...     pass



四、变量的作用域
C/C++里,全局变量在函数内部是完全可见的。但是在Python里,由于是解释型语言,所以不一样了。对于一个变量,如果首次出现在赋值语句的左边,那么会被认为是一个局部变量,和外面的同名全局变量没关系。如果首次出现在赋值语句右边或者其他地方,那么就会被认为是全局变量,直接用全局变量。

>>> lst = [1, 2, 3]
>>> def foo1():
...     lst.append(5)   # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
...     lst += [5]      # ... but this bombs!
...
>>> foo2()


这就是为什么上面两个函数,前者是正确的,后者是错误的。

五、在遍历list的时候修改它
这个错误其实在C++使用迭代器时也会出现。即使用for循环来遍历list时,由于循环之前就确定了循环次数,修改list会导致下标的变化。

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
...     if odd(numbers[i]):
...         del numbers[i]
...
Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
IndexError: list index out of range

使用下面这种高级写法,可以避免此类错误。

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]
>>> numbers
[0, 2, 4, 6, 8]


六、闭包中绑定变量
闭包中的变量的值是内函数 lambda x : i * x  调用的时候才确定的。

>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)


对于上面的程序,不管哪一个内函数被调用时,i的值都是最后的4。因此输出会是8 8 8 8 8,而不是0 2 4 6 8。解决的办法可以利用默认参数来生成匿名函数。

>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)


七、模块之间的循环依赖
Python解释器会去设法避免重复导入模块,但是要注意顺序。


八、小心重名的模块
Python的类库非常丰富,但是要小心重名的模块。

九、不同Python版本的坑
注意Python2和Python3的区别。

十、类的析构函数
当析构函数被调用时,解释器会把变量变为None,这样有些事情就做不了了。

import foo
class Bar(object):
       ...
    def __del__(self):
        foo.cleanup(self.myhandle)

可以使用 atexit.register来引入另一个函数做想做的事情。

import foo
import atexit
def cleanup(handle):
    foo.cleanup(handle)

class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)

本文翻译自,有做增删改:
http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make

你可能感兴趣的:(Python编程中容易出现的10个错误)