Python程序员最常犯的10个错误,你中招了吗?

本文列举了Python开发过程中常见的10个错误,包括滥用表达式作为函数参数的默认值、不理解闭包中的变量绑定等问题,并提供了相应的解决方案。

原文链接

摘要: Python简介 Python是一种具有动态语义的、面向对象的解释型高级编程语言。因其内置了高级数据结构,并支持动态类型和动态绑定,使用Python进行快速应用程序开发十分便利。同时作为一门脚本语言,它兼容部分现有的组件和服务。

Python简介

Python是一种具有动态语义的、面向对象的解释型高级编程语言。因其内置了高级数据结构,并支持动态类型和动态绑定,使用Python进行快速应用程序开发十分便利。同时作为一门脚本语言,它兼容部分现有的组件和服务。Python还支持模块和各种库的扩展,有助于实现模块化编程和提高代码复用率。

关于本文

刚接触这门语言的新手可能会对Python简洁灵活的语法有些不适应,或是低估了Python强大的性能。鉴于此,本文列出了Python开发人员常犯的10个小错误,资深程序猿也难免会中招哦。

0582085fcad54ac457da10cc973b153357fa0658

本文供Python高级开发人员参考,Python小白可以参考下面这篇文章:

http://www.onlamp.com/pub/a/python/2004/02/05/learn_python.html

常见错误1:滥用表达式作为函数参数的默认值

Python允许开发者指定函数参数的默认值,这也是Python的一大特色,但当默认值可变时,可能会给开发者带来一些困扰。例如下面定义的函数:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74"><span style="color:#a6e22e">foo</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">bar</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">=</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">[</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">]</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span> # bar is optional and defaults to <span style="color:#f8f8f2">[</span><span style="color:#f8f8f2">]</span> <span style="color:#66d9ef"><span style="color:#f92672">if</span></span> not specified

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> bar<span style="color:#f8f8f2">.</span><span style="color:#e6db74"><span style="color:#a6e22e">append</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#a6e22e"><span style="color:#f8f8f2"><span style="color:#e6db74">"baz"</span></span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span> # but <span style="color:#f92672">this</span> line could be problematic<span style="color:#f8f8f2">,</span> as we'll see<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">return</span></span> bar</code></span></span>

看出bug了吗?那就是在每次调用函数前没有对可变参数进行赋值,而认为该参数就是默认值。比如上面的代码,有人可能期望在反复调用foo()时返回'baz',以为每次调用foo()时,bar的值都为[],即一个空列表。

但是,让我们来看看代码运行结果:

 
<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">,</span> <span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span>      

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">,</span> <span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">,</span> <span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span></code></span></span>

嗯?为什么每次调用foo()后会不断把"baz"添加到已有的列表,而不是新建一个新列表呢?答案就是,函数参数的默认值仅在定义函数时执行一次。因此,仅在第一次定义foo()时,bar初始化为默认值(即空列表),此后,每次调用foo()函数时,参数bar都是第一次初始化时生成的列表。

常见的解决方案: 

 

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74"><span style="color:#a6e22e">foo</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">bar</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">=</span></span><span style="color:#f8f8f2">None</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">if</span></span> bar is None<span style="color:#f8f8f2">:</span> # or <span style="color:#66d9ef"><span style="color:#f92672">if</span></span> not bar<span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> bar <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> bar<span style="color:#f8f8f2">.</span><span style="color:#e6db74">append</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">return</span></span> bar

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"baz"</span></span><span style="color:#f8f8f2">]</span></code></span></span>

 

常见错误2:错误地使用类变量

代码示例:

 
<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#f92672">class</span> <span style="color:#e6db74"><span style="color:#a6e22e">A</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">object</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#f92672">class</span> <span style="color:#e6db74"><span style="color:#a6e22e">B</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">A</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> pass

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#f92672">class</span> <span style="color:#e6db74"><span style="color:#a6e22e">C</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">A</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> pass

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> print A<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> B<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> C<span style="color:#f8f8f2">.</span>x

<span style="color:#ae81ff">1</span> <span style="color:#ae81ff">1</span> <span style="color:#ae81ff">1</span></code></span></span>

运行结果没问题。

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> B<span style="color:#f8f8f2">.</span>x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> print A<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> B<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> C<span style="color:#f8f8f2">.</span>x

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span></code></span></span>

结果也正确。

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> A<span style="color:#f8f8f2">.</span>x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> print A<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> B<span style="color:#f8f8f2">.</span>x<span style="color:#f8f8f2">,</span> C<span style="color:#f8f8f2">.</span>x

<span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span> <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span></code></span></span>

什么鬼?我们只改变了A.x.,为什么C.x 也变了?

在Python中,类变量是以字典形式进行内部处理,遵循方法解析顺序(Method Resolution Order ,MRO)。因此,在上述代码中,因为在类C中没有找到属性x,它就会从父类中查找x的值(尽管Python支持多重继承,但上述代码只存在一个父类A)。换句话说,C没有独立于类A的属于自己的x。因此,C.x实际上指的是A.x。除非处理得当,否则就会导致Python出现错误。

如果想更深入了解Python的类特性,请戳:

https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide

常见错误3:错误指定异常代码块的参数

假设你有如下代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#f92672">try</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> l <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"a"</span></span><span style="color:#f8f8f2">,</span> <span style="color:#a6e22e"><span style="color:#e6db74">"b"</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">int</span></span><span style="color:#f8f8f2">(</span>l<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">]</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> except ValueError<span style="color:#f8f8f2">,</span> IndexError<span style="color:#f8f8f2">:</span> # To <span style="color:#f92672">catch</span> both exceptions<span style="color:#f8f8f2">,</span> right<span style="color:#f8f8f2">?</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> pass

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

Traceback <span style="color:#f8f8f2">(</span>most recent call last<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

IndexError<span style="color:#f8f8f2">:</span> <span style="color:#e6db74">list</span> index out of range</code></span></span>

这里的问题是except语句不接受以这种方式指定的异常列表。在Python2.x中,except Exception语句中变量e可用来把异常信息绑定到第二个可选参数上,以便进一步查看异常的情况。因此,在上述代码中,except语句并没有捕捉到IndexError异常;而是将出现的异常绑定到了参数IndexError中。

想在一个except语句同时捕捉到多个异常的正确方式是,将第一个参数指定为元组,并将要捕捉的异常类型都写入该元组中。为了方便起见,可以使用as关键字,Python 2 和Python 3都支持这种语法格式:

 

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#f92672">try</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> l <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#a6e22e"><span style="color:#e6db74">"a"</span></span><span style="color:#f8f8f2">,</span> <span style="color:#a6e22e"><span style="color:#e6db74">"b"</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">int</span></span><span style="color:#f8f8f2">(</span>l<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">]</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> except <span style="color:#f8f8f2">(</span>ValueError<span style="color:#f8f8f2">,</span> IndexError<span style="color:#f8f8f2">)</span> as e<span style="color:#f8f8f2">:</span> 

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> pass

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span></code></span></span>

 

常见错误4:错误理解Python中变量的作用域

Python变量作用域遵循LEGB规则,LEGB是Local,Enclosing,Global,Builtin的缩写,分别代表本地作用域、封闭作用域、全局作用域和内置作用域,这个规则看起来一目了然。事实上,Python的这种工作方式较为独特,会导致一些编程错误,例如:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">10</span></span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> x <span style="color:#f8f8f2">+</span><span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> print x

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

Traceback <span style="color:#f8f8f2">(</span>most recent call last<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> in foo

UnboundLocalError<span style="color:#f8f8f2">:</span> local variable <span style="color:#a6e22e"><span style="color:#e6db74">'x'</span></span> referenced before assignment</code></span></span>

问题出在哪?

上面的错误是因为在作用域内对变量赋值时,Python自动将该变量视为该作用域的本地变量,并对外部定义的同名变量进行了屏蔽。因此,原本正确的代码,在某个函数内部添加了一个赋值语句后,却意外收到了UnboundLocalError的报错信息。

关于UnboundLocalError更多内容请戳:

https://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

在使用列表时,Python程序员更容易掉入此类陷阱,例如:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> lst <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74">foo1</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> lst<span style="color:#f8f8f2">.</span><span style="color:#e6db74">append</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">5</span></span><span style="color:#f8f8f2">)</span> # This works ok<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo1</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> lst

<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">5</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> lst <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">3</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74">foo2</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> lst <span style="color:#f8f8f2">+</span><span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">5</span></span><span style="color:#f8f8f2">]</span> # <span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> but <span style="color:#f92672">this</span> bombs<span style="color:#f8f8f2">!</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#e6db74">foo2</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

Traceback <span style="color:#f8f8f2">(</span>most recent call last<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> in foo

UnboundLocalError<span style="color:#f8f8f2">:</span> local variable <span style="color:#a6e22e"><span style="color:#e6db74">'lst'</span></span> referenced before assignment</code></span></span>

奇怪,为什么foo1正常运行,而foo2崩溃了呢?

原因和上一个案例中出现的问题相似,但这里的错误更加细微。函数foo1没有对变量lst进行赋值操作,而函数foo2有赋值操作。

首先, lst += [5]是lst = lst + [5]的缩写形式,在函数foo2中试图对变量lst进行赋值操作(Python将变量lst默认为本地作用域的变量)。但是,lst += [5]语句是对lst变量自身进行的赋值操作(此时变量lst的作用域是函数foo2),但是在函数foo2中还未声明该变量,所以就报错啦!

常见错误5:在遍历列表时修改列表

下面代码中的错误很明显:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> odd <span style="color:#f8f8f2">=</span> lambda x <span style="color:#f8f8f2">:</span> <span style="color:#e6db74"><span style="color:#f92672">bool</span></span><span style="color:#f8f8f2">(</span>x <span style="color:#f8f8f2">%</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> numbers <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span>n <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> n in <span style="color:#e6db74">range</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">10</span></span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> i in <span style="color:#e6db74">range</span><span style="color:#f8f8f2">(</span><span style="color:#e6db74">len</span><span style="color:#f8f8f2">(</span>numbers<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">if</span></span> <span style="color:#e6db74">odd</span><span style="color:#f8f8f2">(</span>numbers<span style="color:#f8f8f2">[</span>i<span style="color:#f8f8f2">]</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> del numbers<span style="color:#f8f8f2">[</span>i<span style="color:#f8f8f2">]</span> # BAD<span style="color:#f8f8f2">:</span> Deleting item from a <span style="color:#e6db74">list</span> <span style="color:#66d9ef"><span style="color:#f92672">while</span></span> iterating over it

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

Traceback <span style="color:#f8f8f2">(</span>most recent call last<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

File <span style="color:#e6db74">"</span><span style="color:#f8f8f2"><span style="color:#e6db74"><</span></span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2"><span style="color:#e6db74">></span></span><span style="color:#e6db74">"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

IndexError<span style="color:#f8f8f2">:</span> <span style="color:#e6db74">list</span> index out of range</code></span></span>

有经验的程序员都知道,在Python中遍历列表或数组时不应该删除该列表(数组)中的元素。虽然上面代码的错误很明显,但是在编写复杂代码时,资深程序员也难免会犯此类错误。

幸好Python集成了大量经典的编程范式,如果运用得当,可以大大简化代码并提高编程效率。简单的代码会降低出现上述bug的几率。列表解析式(list comprehensions)就是利器之一,它将完美避开上述bug,解决方案如下:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> odd <span style="color:#f8f8f2">=</span> lambda x <span style="color:#f8f8f2">:</span> <span style="color:#e6db74"><span style="color:#f92672">bool</span></span><span style="color:#f8f8f2">(</span>x <span style="color:#f8f8f2">%</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> numbers <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span>n <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> n in <span style="color:#e6db74">range</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">10</span></span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> numbers<span style="color:#f8f8f2">[</span><span style="color:#f8f8f2">:</span><span style="color:#f8f8f2">]</span> <span style="color:#f8f8f2">=</span> <span style="color:#f8f8f2">[</span>n <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> n in numbers <span style="color:#66d9ef"><span style="color:#f92672">if</span></span> not <span style="color:#e6db74">odd</span><span style="color:#f8f8f2">(</span>n<span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">]</span> # ahh<span style="color:#f8f8f2">,</span> the beauty of it all

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> numbers

<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">0</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">4</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">6</span></span><span style="color:#f8f8f2">,</span> <span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span><span style="color:#f8f8f2">]</span></code></span></span>

更多有关列表解析式的详细内容,请戳:https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps

常见错误6:不理解Python闭包中的变量绑定

代码示例:

 

 

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74"><span style="color:#a6e22e">create_multipliers</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">return</span></span> <span style="color:#f8f8f2">[</span>lambda x <span style="color:#f8f8f2">:</span> i <span style="color:#f8f8f2">*</span> x <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> i in <span style="color:#e6db74"><span style="color:#a6e22e">range</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#ae81ff"><span style="color:#f8f8f2"><span style="color:#ae81ff">5</span></span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> multiplier in <span style="color:#e6db74"><span style="color:#a6e22e">create_multipliers</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> print <span style="color:#e6db74"><span style="color:#a6e22e">multiplier</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#ae81ff"><span style="color:#f8f8f2"><span style="color:#ae81ff">2</span></span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>
</code></span></span>

 

你以为运行结果会是:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#ae81ff"><span style="color:#ae81ff">0</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">4</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">6</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span></code></span></span>

但实际输出结果是:8

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span>

<span style="color:#ae81ff"><span style="color:#ae81ff">8</span></span></code></span></span>

惊不惊喜!

这种情况是由于Python延迟绑定(late binding)机制造成的,也就是说只有在内部函数被调用时才会搜索闭包中变量的值。所以在上述代码中,每次调用create_multipliers()函数中的return函数时,会在附近作用域中查询变量i的值。(此时,return中循环已结束,所以i值为4)。

常见解决方案:

 

 

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> def <span style="color:#e6db74"><span style="color:#a6e22e">create_multipliers</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> <span style="color:#66d9ef"><span style="color:#f92672">return</span></span> <span style="color:#f8f8f2">[</span>lambda x<span style="color:#f8f8f2">,</span> i<span style="color:#f8f8f2">=</span>i <span style="color:#f8f8f2">:</span> i <span style="color:#f8f8f2">*</span> x <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> i in <span style="color:#e6db74"><span style="color:#a6e22e">range</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#ae81ff"><span style="color:#f8f8f2"><span style="color:#ae81ff">5</span></span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">]</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> <span style="color:#66d9ef"><span style="color:#f92672">for</span></span> multiplier in <span style="color:#e6db74"><span style="color:#a6e22e">create_multipliers</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span> print <span style="color:#e6db74"><span style="color:#a6e22e">multiplier</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#ae81ff"><span style="color:#f8f8f2"><span style="color:#ae81ff">2</span></span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

<span style="color:#ae81ff">0</span>

<span style="color:#ae81ff">2</span>

<span style="color:#ae81ff">4</span>

<span style="color:#ae81ff">6</span>

<span style="color:#ae81ff">8</span>
</code></span></span>

 

没错!我们利用了匿名函数lambda的默认参数来生成结果序列。有人觉得这种用法很简洁,有人会说它很巧妙,还有人会觉得晦涩难懂。如果你是Python开发人员,那么深刻理解上述语法对你而言非常重要。

常见错误7:模块之间出现循环依赖

假设你有两个文件,分别是a.py和b.py,两者相互导入,如下所示:

a.py模块中的代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import b

def <span style="color:#e6db74"><span style="color:#a6e22e">f</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#66d9ef"><span style="color:#f92672">return</span></span> b<span style="color:#f8f8f2">.</span>x

print <span style="color:#e6db74"><span style="color:#a6e22e">f</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span></code></span></span>

b.py模块中的代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import a

x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

def <span style="color:#e6db74">g</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

print a<span style="color:#f8f8f2">.</span><span style="color:#e6db74">f</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span></code></span></span>

首先,我们尝试导入a.py:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> import a

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span></code></span></span>

运行结果正确!这似乎有点出人意料,因为我们在这里进行循环导入,应该会报错呀!

答案是,在Python中如果仅存在一个循环导入,程序不会报错。如果一个模块已经被导入,Python会自动识别而不会再次导入。但是如果每个模块试图访问其他模块不同位置的函数或变量时,那么Error又双叒叕出现了。

回到上面的示例中,当导入a.py模块时,程序可以正常导入b.py模块,因为此时b.py模块未访问a.py中定义任何的变量或函数。b.py模块仅引用了a.py模中的a.f()函数。调用的a.f()函数隶属于g()函数,而a.py或b.py模块中并没有调用g()函数。所以程序没有报错。

但是,如果我们在未导入a.py模块之前先导入b.py模块,结果会怎样?

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> import b

<span style="color:#a6e22e">Traceback</span> <span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">most recent call last</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

File "<span style="color:#f8f8f2"><</span><span style="color:#f92672"><span style="color:#e6db74">stdin</span></span><span style="color:#f8f8f2">></span>"<span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">1</span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

File <span style="color:#a6e22e">"b.py"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">1</span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

import a

File <span style="color:#a6e22e">"a.py"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">6</span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

print <span style="color:#e6db74"><span style="color:#a6e22e">f</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

File <span style="color:#a6e22e">"a.py"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">4</span><span style="color:#f8f8f2">,</span> in f

<span style="color:#66d9ef"><span style="color:#f92672">return</span></span> b<span style="color:#f8f8f2">.</span>x

AttributeError<span style="color:#f8f8f2">:</span> <span style="color:#a6e22e">'module'</span> object has no attribute <span style="color:#a6e22e">'x'</span></code></span></span>

报错了!问题在于,在导入b.py的过程中,它试图导入a.py模块,而a.py模块会调用f()函数,f()函数又试图访问b.x变量。但此时,还未对变量b.x进行定义,所以出现了AttributeError异常。

稍微修改下b.py,即在g()函数内部导入a.py就可以解决上述问题。

修改后的b.py:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">x <span style="color:#f8f8f2">=</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

def <span style="color:#e6db74">g</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>


import a # This will be evaluated only when <span style="color:#e6db74">g</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span> is called

print a<span style="color:#f8f8f2">.</span><span style="color:#e6db74">f</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span></code></span></span>

现在我们再导入b.py模块,就不会报错啦!

 

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c"><span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> import b

<span style="color:#f8f8f2">>></span><span style="color:#f8f8f2">></span> b<span style="color:#f8f8f2">.</span><span style="color:#e6db74">g</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span>

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span> # Printed a first time since module <span style="color:#a6e22e"><span style="color:#e6db74">'a'</span></span> calls <span style="color:#a6e22e"><span style="color:#e6db74">'print f()'</span></span> at the end

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span> # Printed a second time<span style="color:#f8f8f2">,</span> <span style="color:#f92672">this</span> one is our call to <span style="color:#a6e22e"><span style="color:#e6db74">'g'</span></span></code></span></span>

 

常见错误8:文件命名与Python标准库模块的名称冲突

Python的优势之一就是其集成了丰富的标准库。正因为如此,稍不留神就会在为自己的文件命名时与Python自带标准库模块重名。例如,如果你的代码中有一个名为email.py的模块,恰好就和Python标准库中email.py模块重名了。)

上述问题比较复杂。举个例子,在导入模块A的时候,假如该模块A试图导入Python标准库中的模块B,但你已经定义了一个同名模块B,模块A会错误导入你自定义的模块B,而不是Python标准库中的模块B。这种错误很糟糕,因为程序员很难察觉到是因为命名冲突而导致的。

因此,Python程序员要注意避免与Python标准库模块的命名冲突。毕竟,修改自己模块的名称比修改标准库的名称要容易的多!当然你也可以写一份Python改善建议书(Python Enhancement Proposal,PEP)提议修改标准库的名称。

常见错误9:不熟悉Python2和Python3之间的差异

先来看看foo.py文件中的代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import sys

def <span style="color:#e6db74"><span style="color:#a6e22e">bar</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">i</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#66d9ef"><span style="color:#f92672">if</span></span> i <span style="color:#f8f8f2">==</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">:</span>

raise <span style="color:#e6db74">KeyError</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#66d9ef"><span style="color:#f92672">if</span></span> i <span style="color:#f8f8f2">==</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">:</span>

raise <span style="color:#e6db74">ValueError</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">)</span>

def <span style="color:#e6db74">bad</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

e <span style="color:#f8f8f2">=</span> None

<span style="color:#f92672">try</span><span style="color:#f8f8f2">:</span>

<span style="color:#e6db74">bar</span><span style="color:#f8f8f2">(</span><span style="color:#66d9ef"><span style="color:#f92672">int</span></span><span style="color:#f8f8f2">(</span>sys<span style="color:#f8f8f2">.</span>argv<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">]</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">)</span>

except KeyError as e<span style="color:#f8f8f2">:</span>

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e"><span style="color:#e6db74">'key error'</span></span><span style="color:#f8f8f2">)</span>

except ValueError as e<span style="color:#f8f8f2">:</span>

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e"><span style="color:#e6db74">'value error'</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span>e<span style="color:#f8f8f2">)</span>

<span style="color:#e6db74">bad</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span></code></span></span>

在Python 2中,上述代码运行正常

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">$ python foo<span style="color:#f8f8f2">.</span>py <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

key error

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

$ python foo<span style="color:#f8f8f2">.</span>py <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span>

value error

<span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span></code></span></span>

但是在Python 3中运行时:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">$ python3 foo<span style="color:#f8f8f2">.</span>py <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

key error

<span style="color:#a6e22e">Traceback</span> <span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">most recent call last</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

File <span style="color:#a6e22e">"foo.py"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">19</span><span style="color:#f8f8f2">,</span> in <span style="color:#f8f8f2"><</span>module<span style="color:#f8f8f2">></span>

<span style="color:#e6db74"><span style="color:#a6e22e">bad</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

File <span style="color:#a6e22e">"foo.py"</span><span style="color:#f8f8f2">,</span> line <span style="color:#ae81ff">17</span><span style="color:#f8f8f2">,</span> in bad

<span style="color:#e6db74"><span style="color:#a6e22e">print</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">e</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

UnboundLocalError<span style="color:#f8f8f2">:</span> local variable <span style="color:#a6e22e">'e'</span> referenced before assignment</code></span></span>

什么情况?原来,在Python 3中,在except代码块作用域外无法访问异常对象。(原因是,Python 3会将内存堆栈中的循环引用进行保留,直到垃圾回收器运行后在内存中对其进行清理。)

更多内容请戳:

https://docs.python.org/3/reference/compound_stmts.html#except

解决方法之一是,在except代码块的作用域之外,加一句异常对象的引用就可以正常访问异常对象了。下面是处理后的代码,在Python2和Python3中的运行结果一致:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import sys

def <span style="color:#e6db74"><span style="color:#a6e22e">bar</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">i</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#66d9ef"><span style="color:#f92672">if</span></span> i <span style="color:#f8f8f2">==</span> <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">:</span>

raise <span style="color:#e6db74">KeyError</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#66d9ef"><span style="color:#f92672">if</span></span> i <span style="color:#f8f8f2">==</span> <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">:</span>

raise <span style="color:#e6db74">ValueError</span><span style="color:#f8f8f2">(</span><span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span><span style="color:#f8f8f2">)</span>

def <span style="color:#e6db74">good</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">:</span>

exception <span style="color:#f8f8f2">=</span> None

<span style="color:#f92672">try</span><span style="color:#f8f8f2">:</span>

<span style="color:#e6db74">bar</span><span style="color:#f8f8f2">(</span><span style="color:#66d9ef"><span style="color:#f92672">int</span></span><span style="color:#f8f8f2">(</span>sys<span style="color:#f8f8f2">.</span>argv<span style="color:#f8f8f2">[</span><span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span><span style="color:#f8f8f2">]</span><span style="color:#f8f8f2">)</span><span style="color:#f8f8f2">)</span>

except KeyError as e<span style="color:#f8f8f2">:</span>

exception <span style="color:#f8f8f2">=</span> e

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e"><span style="color:#e6db74">'key error'</span></span><span style="color:#f8f8f2">)</span>

except ValueError as e<span style="color:#f8f8f2">:</span>

exception <span style="color:#f8f8f2">=</span> e

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span><span style="color:#a6e22e"><span style="color:#e6db74">'value error'</span></span><span style="color:#f8f8f2">)</span>

<span style="color:#e6db74">print</span><span style="color:#f8f8f2">(</span>exception<span style="color:#f8f8f2">)</span>

<span style="color:#e6db74">good</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span></code></span></span>

再次在Python3中运行代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">$ python3 foo<span style="color:#f8f8f2">.</span>py <span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

key error

<span style="color:#ae81ff"><span style="color:#ae81ff">1</span></span>

$ python3 foo<span style="color:#f8f8f2">.</span>py <span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span>

value error

<span style="color:#ae81ff"><span style="color:#ae81ff">2</span></span></code></span></span>

问题解决了!

更多有关Python2和Python3之间的区别,请戳:

https://www.toptal.com/python#hiring-guide

常见错误10:误用_del_方法

假设名为mod.py的文件中有如下代码:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import foo

<span style="color:#f92672">class</span> <span style="color:#e6db74"><span style="color:#a6e22e">Bar</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">object</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

def <span style="color:#e6db74">__<span style="color:#a6e22e">del__</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">self</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

foo<span style="color:#f8f8f2">.</span><span style="color:#e6db74"><span style="color:#a6e22e">cleanup</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">self</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">.</span></span><span style="color:#f8f8f2">myhandle</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span></code></span></span>

然后,你想在another_mod.py文件中进行如下操作:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import mod

mybar <span style="color:#f8f8f2">=</span> mod<span style="color:#f8f8f2">.</span><span style="color:#e6db74">Bar</span><span style="color:#f8f8f2">(</span><span style="color:#f8f8f2">)</span></code></span></span>

如果你试图运行another_mod.py,将会出现AttributeError异常。

为什么呢?因为当Python解释器关闭时,该模块的全局变量的值都会被置为None。因此,在上述示例中,在调用__del__函数时,foo的值已经为None。

关于Python解释器的更多内容,请戳:

https://mail.python.org/pipermail/python-bugs-list/2009-January/069209.html

调用atexit.register()函数可以解决上述的Python高阶编程问题。在调用atexit.register()函数后,当你的代码运行结束后(即正常退出程序的情况下),注册处理程序会在解释器关闭之前运行。

应用上述方法,修改后的mod.py文件如下:

<span style="color:#333333"><span style="color:#f8f8f2"><code class="language-c">import foo

import atexit

def <span style="color:#e6db74"><span style="color:#a6e22e">cleanup</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">handle</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

foo<span style="color:#f8f8f2">.</span><span style="color:#e6db74"><span style="color:#a6e22e">cleanup</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">handle</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span>

<span style="color:#f92672">class</span> <span style="color:#e6db74"><span style="color:#a6e22e">Bar</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">object</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

def <span style="color:#e6db74">__<span style="color:#a6e22e">init__</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">self</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span><span style="color:#f8f8f2">:</span>

<span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span><span style="color:#f8f8f2">.</span>

atexit<span style="color:#f8f8f2">.</span><span style="color:#66d9ef"><span style="color:#a6e22e">register</span></span><span style="color:#f8f8f2"><span style="color:#f8f8f2">(</span></span><span style="color:#f8f8f2">cleanup</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">,</span></span><span style="color:#f8f8f2"> self</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">.</span></span><span style="color:#f8f8f2">myhandle</span><span style="color:#f8f8f2"><span style="color:#f8f8f2">)</span></span></code></span></span>

当程序正常终止时,这种方法可以很方便的调用程序的清理功能。上述示例中,foo.cleanup函数会决定如何处理self.myhandle所绑定的对象,但是调用atexit.register()函数就可以由你决定何时执行清理功能。

总结

Python是一种强大且灵活的编程语言,提供了很多编程机制和范式,它可以极大地提高我们的工作效率。但不论使用何种软件工具或编程语言,开发人员都应该彻底理解Python的语法规则和编程规范,否则将会陷入“一知半解,害已误人”的状态。

不断学习Python的语法规则,尤其文中提到这些问题,有助于降低代码的出错概率,也会提升Python编程的效率。

 

原文发布时间为:2018-04-5

本文作者:文摘菌

本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“大数据文摘”微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值