Master the 10 Most Common Python Programming Problems - 10大最常见的Python编程错误

http://blog.csdn.net/pipisorry/article/details/45175457

Introduction

本文介绍python编程中很难捕捉10大错误

(Note: This article is intended for a more advanced audience than Common Mistakes of Python Programmers, which is
geared(适合) more toward those who are newer to the language.)

Common Mistake #1: Misusing expressions as defaults for function arguments

Python allows you to specify(指定) that a function argument isoptional by providing a
default value for it. While this is a greatfeature(特色) of the language, it can lead to someconfusion(混淆)
when the default value ismutable. For example, consider this Python functiondefinition(定义):

>>> def foo(bar=[]):        # bar is optional and defaults to [] if not specified
...    bar.append("baz")    # but this line could be problematic, as we‘ll see...
...    return bar

A common mistake is to think that the
optional(可选择的) argument will be set to the specified default expressioneach time the function is called without supplying a value for the optional argument. In the above code, for example,
one might expect that callingfoo() repeatedly (i.e., without
specifying(指定) a
bar
argument) would always return ‘baz‘, since the
assumption(假定) would be thateach time
foo() is called (without a bar argument specified)bar is set to
[] (i.e., a new empty list).

But let’s look at what actually happens when you do this:

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

Huh? Why did it keep appending(附加) the default value of"baz" to an
existing list each time foo() was called, rather than creating anew list each time?

The more advanced Python programming answer is that the default value for a function argument is onlyevaluated(评价) once, at the time that the
function isdefined(定义).
Thus, thebar argument is
initialized(初始化) to its default (i.e., an empty list) only whenfoo() is first defined, but then calls to
foo() (i.e., without abar argument
specified(规定的)) will continue to use the same list to whichbar was originally initialized.

FYI, a common workaround(工作区) for this is as follows:

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

Common Mistake #2: Using classvariables(变量) incorrectly

Consider the following example:

>>> class A(object):
...     x = 1
...
>>> class B(A):
...     pass
...
>>> class C(A):
...     pass
...
>>> print A.x, B.x, C.x
1 1 1

Makes sense.

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1

Yup, again as expected.

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3

What the $%#!&?? We only changed A.x. Why did C.x change too?

In Python, class variables(变量) areinternally(内部地)
handled as dictionaries and follow what is often referred to asMethod Resolution Order (MRO). So in the above code, since the
attribute(属性)
x is not found in class C, it will be looked up in its base classes (onlyA in the above example, although Python supports multiple
inheritance(继承)). In other words,C doesn’t have its own
x property, independent of A. Thus,
references(参考) toC.x are in fact references to
A.x. This causes a Python problem unless it’s handled properly. Learn more aoutclass
attributes(属性) in Python
.

Common Mistake #3:
Specifying(指定)
parameters(参数) incorrectly for anexception(例外) block

Suppose you have the following code:

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError:  # To catch both exceptions, right?
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

The problem here is that the except statement does not take a list of exceptions specified in this manner. Rather, In Python 2.x, thesyntax(语法)except
Exception, e
is used to bind(绑) the exception to theoptional second
parameter(参数)specified(指定) (in this casee),
in order to make it available for further
inspection(视察). As a result, in the above code, theIndexError exception is
not being caught by the exceptstatement(声明); rather, theexception(例外)
instead ends up being bound to a parameter namedIndexError.

The proper way to catch multiple exceptions in an except statement is to specify the first parameter as atuple containing all exceptions
to be caught. Also, for maximum portability(可移植性), use theas keyword, since that
syntax(语法) is supported by both Python 2 and Python 3:

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

Common Mistake #4: Misunderstanding Pythonscope(范围) rules

Python(巨蟒) scoperesolution(分辨率) is based
on what is known as theLEGB rule, which is
shorthand(速记法的) forLocal,
Enclosing, Global, Built-in. Seems
straightforward(简单的) enough, right? Well, actually, there are somesubtleties(微妙)
to the way this works in Python, which brings us to the common more advanced Python programming problem below. Consider the following:

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

What’s the problem?

The above error occurs because, when you make an assignment to a
variable(变量的) in ascope(范围),that variable is
automatically(自动地) considered by Python to be local to that scope
and shadows any similarly named variable in any outer scope.

Many are thereby surprised to get an UnboundLocalError in previously working code when it ismodified(修改) by adding anassignment(分配)statement(声明)
somewhere in the body of a function. (You can read more about thishere.)

It is particularly common for this to trip up developers when using lists. Consider the following example:

>>> 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()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable ‘lst‘ referenced before assignment

Huh? Why did foo2 bomb while foo1 ran fine?

The answer is the same as in the prior(优先的) example problem, but is admittedly moresubtle(微妙的).foo1
is not making an assignment to lst, whereasfoo2 is. Remembering that
lst += [5] is really just
shorthand(速记法的) forlst = lst + [5], we see that we are attempting to
assign a value tolst (therefore
presumed(假定) by Python to be in the localscope(范围)). However, the value we are looking
toassign(分配) tolst is based on
lst itself (again, now presumed to be in the local scope), which has not yet beendefined(定义).Boom(兴旺).

Common Mistake #5: Modifying a list whileiterating(迭代)
over it

The problem with the following code should be fairly obvious:

>>> 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]  # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
  	  File "<stdin>", line 2, in <module>
IndexError: list index out of range

Deleting an item from a list or array(数组) while iterating over it is a Python problem that is well known to any experienced software developer. But
while the example above may be fairly obvious, even advanced developers can be
unintentionally(无意地) bitten by this in code that is much morecomplex(复杂的).

Fortunately, Python incorporates(包含) a number ofelegant(高雅的)
programmingparadigms(范例) which, when used properly, can result insignificantly(意味深长地)simplified(简化了的)
andstreamlined(流线型的) code. A sidebenefit(利益)
of this is that simpler code is less likely to be bitten by the accidental-deletion-of-a-list-item-while-iterating-over-it bug. One such paradigm is that of
list comprehensions. Moreover, list
comprehensions(理解) are particularly useful for avoiding thisspecific(特殊的) problem,
as shown by thisalternate(交替的)implementation(实现)
of the above code which works perfectly:

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]  # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]

Common Mistake #6: Confusing how Pythonbinds(捆绑)variables(变量)
inclosures(关闭)

Considering the following example:

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

You might expect the following output(输出):

0
2
4
6
8

But you actually get:

8
8
8
8
8

Surprise!

This happens due to Python’s late binding
behavior(行为) which says that the values ofvariables(变量) used inclosures(关闭)
are looked up at the time the inner function is called. So in the above code, whenever any of the returned functions are called, the value of
i is looked up in the surrounding
scope(范围) at the time it is called
(and by then, theloop(环) has completed, soi
has already been assigned(分配) its final value of 4).

The solution(解决方案) to this common Python problem is a bit of a hack:

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

Voilà! We are taking advantage of default arguments here to
generate(形成)
anonymous(匿名的) functions in order to achieve the desired behavior. Some would call thiselegant(高雅的).
Some would call itsubtle(微妙的). Some hate it. But if you’re a Python developer, it’s important to understand in any case.

Common Mistake #7: Creatingcircular(通知) moduledependencies(依赖性)

Let’s say you have two files, a.py and b.py, each of which imports the other, as follows:

In a.py:

import b

def f():
    return b.x

print f()

And in b.py:

import a

x = 1

def g():
    print a.f()

First, let’s try importing a.py:

>>> import a
1

Worked just fine. Perhaps that surprises you. After all, we do have a
circular(循环的) import here whichpresumably(大概) should be a problem, shouldn’t it?

The answer is that the mere(仅仅的)presence of a circular import is not in and of itself a problem in Python. If a module has already been
imported, Python is smart enough not to try to re-import it. However, depending on the point at which each module is attempting to access functions or
variables(变量)defined(定义) in the other,
you may indeed run into problems.

So returning to our example, when we imported a.py, it had no problem importingb.py, since
b.py does not require anything from a.py to be definedat the time it is imported. The only
reference(参考) inb.py to
a is the call to a.f(). But that call is ing() and nothing in
a.py or b.py invokes g(). So life is good.

But what happens if we attempt to import b.py (without having previously importeda.py, that is):

>>> import b
Traceback (most recent call last):
  	  File "<stdin>", line 1, in <module>
  	  File "b.py", line 1, in <module>
    import a
  	  File "a.py", line 6, in <module>
	print f()
  	  File "a.py", line 4, in f
	return b.x
AttributeError: ‘module‘ object has no attribute ‘x‘

Uh-oh(噢喔). That’s not good! The problem here is that, in the process of importingb.py, it attempts to import
a.py, which in turn calls f(), which attempts to access
b.x. But b.x has not yet beendefined(定义).Hence(因此)
theAttributeError exception.

At least one solution(解决方案) to this is quitetrivial(不重要的).
Simplymodify(修改)b.py to import
a.py within g():

x = 1

def g():
    import a	# This will be evaluated only when g() is called
    print a.f()

No when we import it, everything is fine:

>>> import b
>>> b.g()
1	# Printed a first time since module ‘a‘ calls ‘print f()‘ at the end
1	# Printed a second time, this one is our call to ‘g‘

Common Mistake #8: Nameclashing(冲突) with Python
Standard Library modules

One of the beauties of Python is the wealth of library modules that it comes with “out of the box”. But as a result, if you’re notconsciously(自觉地)
avoiding it, it’s not that difficult to run into a name clash between the name of one of your modules and a module with the same name in the standard library that ships with Python (for example, you might have a module namedemail.py in your code,
which would be in conflict(冲突) with the standard library module of the same name).

This can lead to gnarly(多瘤的) problems, such as importing another library which in turns tries to import the Python Standard Library version of a
module but, since you have a module with the same name, the other package mistakenly imports your version instead of the one within the Python Standard Library. This is where bad Python errors happen.

Care should therefore be exercised to avoid using the same names as those in the Python Standard Library modules. It’s way easier for you to change the name of a module within your package than it is to file aPython(巨蟒)
Enhancement Proposal (PEP)
to request a name changeupstream(上游部门) and to try and get thatapproved(批准).

Common Mistake #9: Failing to address differences between Python 2 and Python 3

Consider the following file foo.py:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def bad():
    e = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        print(‘key error‘)
    except ValueError as e:
        print(‘value error‘)
    print(e)

bad()

On Python 2, this runs fine:

$ python foo.py 1
key error
1
$ python foo.py 2
value error
2

But now let’s give it a whirl(旋转) on Python 3:

$ python3 foo.py 1
key error
Traceback (most recent call last):
  File "foo.py", line 19, in <module>
    bad()
  File "foo.py", line 17, in bad
    print(e)
UnboundLocalError: local variable ‘e‘ referenced before assignment

What has just happened here? The “problem” is that, in Python 3, the
exception(例外) object is notaccessible(易接近的) beyond thescope(范围)
of theexcept block. (The reason for this is that, otherwise, it would keep areference(参考) cycle with thestack(堆)frame(框架)
in memory until the garbagecollector(收藏家) runs andpurges(净化)
the references from memory. More technical detail about this is availablehere).

One way to avoid this issue is to
maintain(维持) a
reference(参考) to theexception(例外) objectoutside the
scope(范围) of theexcept block so that it remains
accessible(易接近的). Here’s a version of the previous example that uses this technique, therebyyielding(屈服)
code that is both Python 2 and Python 3 friendly:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def good():
    exception = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        exception = e
        print(‘key error‘)
    except ValueError as e:
        exception = e
        print(‘value error‘)
    print(exception)

good()

Running this on Py3k:

$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2

Yippee!

(Incidentally, our Python Hiring Guide discusses a number of other important differences to be
aware(意识到的) of whenmigrating(移动) code from Python 2 to Python 3.)

Common Mistake #10: Misusing the__del__ method

Let’s say you had this in a file called mod.py:

import foo

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

And you then tried to do this from another_mod.py:

import mod
mybar = mod.Bar()

You’d get an ugly AttributeError exception.

Why? Because, as reported here, when the interpreter(解释者) shuts down, the module’s globalvariables(变量)
are all set toNone. As a result, in the above example, at the point that
__del__ is invoked(调用), the namefoo has already been set to
None.

A solution(解决方案) to this somewhat more advanced Python programming problem would be to useatexit.register()
instead. That way, when your program is finishedexecuting(实行) (when exiting normally, that is), your registered handlers are kicked offbefore
the interpreter is shut down.

With that understanding, a fix for the above mod.py code might then look something like this:

import foo
import atexit

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

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

This implementation(实现) provides a clean andreliable(可靠的)
way of calling any neededcleanup(第四位击球员的)functionality(功能)
upon normal programtermination(结束). Obviously, it’s up tofoo.cleanup to decide what to do with the object bound to the name
self.myhandle, but you get the idea.

Wrap-up

Python is a powerful andflexible(灵活的) language with manymechanisms(机制)
andparadigms(范例) that can greatly improveproductivity(生产力).
As with any software tool or language, though, having a limited understanding orappreciation(欣赏) of itscapabilities(才能)
can sometimes be more of animpediment(口吃) than abenefit(利益),
leaving one in theproverbial(谚语的) state of “knowing enough to be dangerous”.

Familiarizing(熟悉) oneself with the keynuances(细微差别)
of Python, such as (but by no means limited to) themoderately(适度地) advanced programming problems raised in this article, will helpoptimize(最优化)
use of the language while avoiding some of its more common errors.

You might also want to check out our Insider’s Guide to Python Interviewing for suggestions on interview questions that can helpidentify(确定) Python experts.

from:http://blog.csdn.net/pipisorry/article/details/45175457

ref:http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make

时间: 2024-10-07 08:55:55

Master the 10 Most Common Python Programming Problems - 10大最常见的Python编程错误的相关文章

[ZZ]10 Most Common Mistakes that Python Programmers Make

About Python Python is an interpreted, object-oriented, high-level programming language with dynamic semantics. Its high-level built in data structures, combined with dynamic typing and dynamic binding, make it very attractive for Rapid Application D

Building Python 2.7.10 with Visual Studio 2010 or 2015 - Google Chrome

您的浏览器(Chrome 33) 需要更新.该浏览器有诸多安全漏洞,无法显示本网站的所有功能.?了解如何更新浏览器 × p-nand-q.com C++? Python? Programming? Languages? Humor? Tools? Misc? Building Python 2.7.10 with Visual Studio 2010 or 2015 7th revision, August 7, 2015.A document history can be found at t

Python学习第10天

事件驱动 简而言之,事件驱动分为二个部分:第一,注册事件:第二,触发事件. 自定义事件驱动框架 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #!/usr/bin/env python 4 # -*- coding:utf-8 -*- 5 6 # event_drive.py 7 event_list = [] 8 def run(): 9 for event in event_list: 10 obj = event() 11 obj.e

Python Special Syntax 10:列表综合,不定数参数传递(C#的params关键字)

列表综合 参见:http://woodpecker.org.cn/abyteofpython_cn/chinese/ch15s03.html #-*-coding:utf-8 def ttt(value1,*args): for i in args: value1+=i print(value1) def tt2(**args): iSum=args['a']+args['b'] print iSum ttt(10,1,2,3,4) tt2(a=3,b=4) 输出:207 Python Spec

python 批量生产10万接入用户

#-*- conding:utf-8 -*- #version:python 2.7.10 import sys import os import time def User():     txt = open('e:/python/pytest/user.txt','w')     now = time.time()     for i in range(1000000):         txt.write("user%s  time=%s\n" %((i+1),time.time

【从零学习Python】Ubuntu14.10下Python开发环境配置

1. 前言 近期在研究计算机视觉的一些算法,也刚開始接触linux,试着在ubuntu下用qt+openCV进行开发,感觉还行.可是Python作为在学术领域广为应用的高级解释性语言.其在计算机视觉的领域也应用得很广泛.于是我就买了一本<python计算机视觉>的书,打算从零開始自学python,而且先主要尝试用python进行cv方面的开发. 2. 开发环境 我如今使用的是眼下最新的64位的Ubuntu14.10系统,不同的版本号配置方法上可能会有些许的差异 首先当然是先安装python包,

Python快速学习10: 循环的对象及设计 (生活的规律)

前言 系列文章:[传送门] 生活逐渐规律,按时睡觉.今天写博客,明天补时间看会书.慢慢的时间很珍惜 我很喜欢! 时钟就像个循环体,我们将它融入生活. 正文 循环对象的并不是随着Python的诞生就存在的,但它的发展迅速,特别是Python 3x的时代,循环对象正在成为循环的标准形式. 灵活的循环方式 (我晚饭后爱上了萨克斯,因为这是生活的一部分.属于我的特殊循环对象,它的按键就像循环方式,然后出来一首美丽的歌曲) 我的萨克斯偶像 循环对象 循环对象是这样一个对象,它包含有一个next()方法(_

Python基础(10)--数字

本文的主题是 Python 中的数字.会详细介绍每一种数字类型,它们适用的各种运算符, 以及用于处理数字的内建函数.在文章的末尾, 简单介绍了几个标准库中用于处理数字的模块. 本文地址:http://www.cnblogs.com/archimedes/p/python-number.html,转载请注明源地址. 数字类型 数字提供了标量贮存和直接访问.它是不可更改类型,也就是说变更数字的值会生成新的对象.当然,这个过程无论对程序员还是对用户都是透明的,并不会影响软件的开发方式. Python

数学之路-python计算实战(10)-机器视觉-透视投影

透视投影 三维计算机图形学中另外一种重要的变换是透视投影.与平行投影沿着平行线将物体投影到图像平面上不同,透视投影按照从投影中心这一点发出的直线将物体投影到图像平面.这就意味着距离投影中心越远投影越小,距离越近投影越大. 最简单的透视投影将投影中心作为坐标原点,z = 1 作为图像平面,这样投影变换为 ; ,用齐次坐标表示为: (这个乘法的计算结果是  = .) 在进行乘法计算之后,通常齐次元素 wc 并不为 1,所以为了映射回真实平面需要进行齐次除法,即每个元素都除以 wc: 更加复杂的透视投