对于Python
Python是一种解释性、面向对象并具备动静语义的高级程序语言。它内建了高级的数据结构,联合了动静类型和动静绑定的长处,这使得它在疾速利用开发中十分有吸引力,并且可作为脚本或胶水语言来连贯现有的组件或服务。Python 反对模块和包,从而激励了程序的模块化和代码重用。
对于这篇文章
Python简略易学的语法可能会使Python开发者——尤其是那些编程的初学者漠视了它的一些奥妙的中央并低估了这门语言的能力。
有鉴于此,本文列出了一个“10强”名单,枚举了甚至是高级Python开发人员有时也难以捕获的谬误。
1: 滥用表达式作为函数参数的默认值
Python容许为函数的参数提供默认的可选值。只管这是语言的一大特色,然而它可能会导致一些易变默认值的凌乱。例如,看一下这个Python函数的定义:
\>>> def foo(bar=\[\]): bar.append("baz") return bar
一个常见的谬误是认为在函数每次不提供可选参数调用时可选参数将设置为默认指定值。在下面的代码中,例如,人们可能会心愿重复(即不明确指定bar参数)地调用foo()时总返回'baz',因为每次foo()调用时都假设(不设定bar参数)bar被设置为[](即一个空列表)。
\>>> foo() \["baz"\] \>>> foo() \["baz", "baz"\] \>>> foo() \["baz", "baz", "baz"\]
耶?为什么每次foo()调用时都要把默认值"baz"追加到现有列表中而不是创立一个新的列表呢?
答案默认参数在定义时求值(比如说当你首次导入模块时)。因而,bar参数在初始化时为其默认值(即一个空列表),即foo()首次定义的时候,但当调用foo()时(即,不指定bar参数时)将持续应用bar本来曾经初始化的参数。
上面是一个常见的解决办法:
\>>> def foo(bar=None): if bar is None: bar = \[\] bar.append("baz") return bar \>>> foo() \["baz"\] \>>> foo() \["baz"\] \>>> foo() \["baz"\]
小编补充:
另外一个常见的例子就是默认参数是一个表达式,如:
import datetime def log(message, time=datetime.datetime.now()): print("{0}: {1}".format(time, message))
冀望的是每次记录不同的工夫,然而未能如愿,记录的是同一个工夫。运行如下代码:
import time log('message 1') time.sleep(1) log('message 2') time.sleep(1) log('message 3') time.sleep(1)
后果如下:
2017-07-25 20:53:20.225000: message 1
2017-07-25 20:53:20.225000: message 2
2017-07-25 20:53:20.225000: message 3
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
后果仿佛有点出其不意了。
咱们只改了A.x,为什么C.x也改了?
在Python中,类变量在外部当做字典来解决,其遵循常被援用的办法解析程序(MRO)。所以在下面的代码中,因为class C中的x属性没有找到,它会向上找它的基类(只管Python反对多重继承,但下面的例子中只有A)。换句话说,class C中没有它本人的x属性,其独立于A。因而,C.x事实上是A.x的援用。
3: 为 except 指定谬误的参数
假如你有如下一段代码:
\>>> try: l = \["a", "b"\] int(l\[2\]) except ValueError, IndexError: pass Traceback (most recent call last): File "<stdin>", line 3, in <module> IndexError: list index out of range
这里的问题在于 except 语句并不承受以这种形式指定的异样列表。相同,在Python 2.x中,应用语法 except Exception, e 是将一个异样对象绑定到第二个_可选_参数(在这个例子中是 e)上,以便在前面应用。所以,在下面这个例子中,IndexError 这个异样并不是被except语句捕捉到的,而是被绑定到一个名叫 IndexError的参数上时引发的。
在一个except语句中捕捉多个异样的正确做法是将第一个参数指定为一个含有所有要捕捉异样的元组。并且,为了代码的可移植性,要应用as关键词,因为Python 2 和Python 3都反对这种语法:
\>>> try: l = \["a", "b"\] int(l\[2\]) except (ValueError, IndexError) as e: pass \>>>
4: 不了解Python的作用域
Python是基于 LEGB 来进行作用于解析的, LEGB是 Local, Enclosing, Global, Built-in 的缩写。看起来“见文知意”,对吗?实际上,在Python中还有一些须要留神的中央,先看上面一段代码:
\>>> x = 10 \>>> def foo(): x += 1 print x \>>> foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment
这里出什么问题了?
下面的问题之所以会产生是因为当你给作用域中的一个变量赋值时,Python 会主动的把它当做是以后作用域的局部变量,从而会暗藏内部作用域中的同名变量。
很多人会感到很吃惊,当他们给之前能够失常运行的代码的函数体的某个中央增加了一句赋值语句之后就失去了一个 UnboundLocalError 的谬误。
尤其是当开发者应用列表 list 时,这个问题就更加常见. 请看上面这个例子:
\>>> lst = \[1, 2, 3\] \>>> def foo1(): lst.append(5) \# 没有问题... \>>> foo1() \>>> lst \[1, 2, 3, 5\] \>>> lst = \[1, 2, 3\] \>>> def foo2(): lst += \[5\] \# ... 然而这里有问题! \>>> foo2() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment
嗯?为什么 foo2 报错,而 foo1 没有问题呢?
起因和之前那个例子的一样,不过更加令人难以捉摸。foo1 没有对 lst 进行赋值操作,而 foo2 做了。要晓得, lst += [5] 是 lst = lst + [5] 的缩写,咱们试图对 lst 进行赋值操作(Python把它当成了局部变量)。此外,咱们对 lst 进行的赋值操作是基于 lst 本身(这再一次被Python当成了局部变量),但此时还未定义。因而出错!
5:在迭代时批改列表 (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
在迭代的时候,从一个列表或者数组中删除元素,对于任何有教训的开发者来说,这是一个家喻户晓的谬误。只管下面的例子非常明显,然而许多高级开发者在更简单的代码中也并非是成心而为之的。
侥幸的是,Python蕴含大量简洁优雅的编程范例,若应用切当,能大大简化和精炼代码。这样的益处是能失去更简化和更精简的代码,能更好的防止程序中呈现当迭代时批改一个列表这样的bug。一个这样的范例是列表生成式(list comprehensions)。而且,列表生成式针对这个问题是特地有用的,通过更改上文中的实现,失去一段极佳的代码:
\>>> 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\]
看完上半局部,这些坑你踩过吗?