当前位置:  开发笔记 > 编程语言 > 正文

Python 2.x陷阱和地雷

如何解决《Python2.x陷阱和地雷》经验,为你挑选了15个好方法。

我的问题的目的是通过Python加强我的知识库并更好地了解它,包括了解它的错误和意外.为了保持特定的内容,我只对CPython解释器感兴趣.

我正在寻找类似于从我的PHP地雷 问题中学到的东西,其中一些答案对我来说是众所周知的但是一对夫妇的边界恐怖.

更新:显然有一两个人感到不安,我问了一个已经在Stack Overflow之外部分回答的问题.这里的某些妥协是URL http://www.ferg.org/projects/python_gotchas.html

请注意,此处的一个或两个答案已经是上面引用的网站上的原始答案.



1> Garth Kidd..:

默认参数中的表达式是在定义函数时计算的,而不是在调用函数时计算的.

示例:考虑将参数默认为当前时间:

>>>import time
>>> def report(when=time.time()):
...     print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19

这个when论点没有改变.定义函数时会对其进行评估.在重新启动应用程序之前,它不会更改.

策略:如果你默认参数None,那么你不会绊倒它,然后在看到它时做一些有用的事情:

>>> def report(when=None):
...     if when is None:
...         when = time.time()
...     print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23

练习:确保你明白:为什么会这样?

>>> def spam(eggs=[]):
...     eggs.append("spam")
...     return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']


Python设计师做了很多很好的设计决策,但这不是其中之一.+1

2> DzinX..:

您应该知道如何在Python中处理类变量.考虑以下类层次结构:

class AAA(object):
    x = 1

class BBB(AAA):
    pass

class CCC(AAA):
    pass

现在,检查以下代码的输出:

>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
>>> print AAA.x, BBB.x, CCC.x
3 2 3

惊讶吗?如果你记得类变量在内部被处理为类对象的字典,你将不会成为.对于读取操作,如果在当前类的字典中找不到变量名,则会搜索父类.所以,再次提供以下代码,但有解释:

# AAA: {'x': 1}, BBB: {}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
# AAA: {'x': 1}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
# AAA: {'x': 3}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
3 2 3

在类实例中处理类变量也是如此(将此示例视为上述示例的延续):

>>> a = AAA()
# a: {}, AAA: {'x': 3}
>>> print a.x, AAA.x
3 3
>>> a.x = 4
# a: {'x': 4}, AAA: {'x': 3}
>>> print a.x, AAA.x
4 3



3> Richard Leva..:

循环和lambdas(或任何闭包,真的):变量由名称绑定

funcs = []
for x in range(5):
  funcs.append(lambda: x)

[f() for f in funcs]
# output:
# 4 4 4 4 4

解决方法是创建单独的函数或按名称传递args:

funcs = []
for x in range(5):
  funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4



4> Algorias..:

动态绑定使变量名称中的拼写错误很难找到.修理一个微不足道的bug很容易花半个小时.

编辑:一个例子......

for item in some_list:
    ... # lots of code
... # more code
for tiem in some_other_list:
    process(item) # oops!


您可以使用像PyLint这样的静态检查器来查找这些错误 - "tiem"将被标记为未使用的变量.
我想是这样,但这只是为了说明的缘故.这种类型的错误的实际发生往往涉及更多.

5> Sven Marnach..:

我用Python遇到的最大惊喜之一是这一个:

a = ([42],)
a[0] += [43, 44]

除了在更新元组的第一个条目后引发TypeError之外,这可以正常工作!因此,a([42, 43, 44],)在执行后+=声明,但会有一个例外呢.如果你试试另一方面

a = ([42],)
b = a[0]
b += [43, 44]

你不会得到错误.


或者你可以简单地写:`a [0] .extend([43,44])`.
哇。我考虑过之后在Python中修改和引发异常。为什么这可能只是疣?

6> 小智..:
try:
    int("z")
except IndexError, ValueError:
    pass

这不起作用的原因是因为IndexError是您正在捕获的异常类型,而ValueError是您要为其分配异常的变量的名称.

用于捕获多个异常的正确代码是:

try:
    int("z")
except (IndexError, ValueError):
    pass



7> Tom Dunham..:

前面有很多关于隐藏语言功能的讨论:隐藏的python功能.提到了一些陷阱(以及一些好东西).

您也可以查看Python Warts.

但对我来说,整数除法是一个问题:

>>> 5/2
2

你可能想要:

>>> 5*1.0/2
2.5

如果你真的想要这个(类C)行为,你应该写:

>>> 5//2
2

因为它也适用于浮点数(当你最终进入Python 3时它会起作用):

>>> 5*1.0//2
2.0

GvR解释了整数除法如何在Python的历史中起作用.


"正确的解决方法是微妙的:如果它可能是一个复数,则向float()抛出一个参数是错误的;如果参数的值为零,则向参数添加0.0不会保留参数的符号.下行是将一个参数(通常是第一个)乘以1.0.这使得float和complex的值和符号保持不变,并将int和long转换为具有相应值的float." (PEP 238 - http://www.python.org/dev/peps/pep-0238/)
绝对是个问题.除了将"from __future__ import division"添加到我创建的每个新的.py文件实际上是一种反射之外,它已经得到了解决.

8> Viktiglemma..:

列表切片给我带来了很多悲伤.我实际上认为以下行为是一个错误.

定义列表x

>>> x = [10, 20, 30, 40, 50]

访问索引2:

>>> x[2]
30

如你所料.

将列表从索引2切割到列表末尾:

>>> x[2:]
[30, 40, 50]

如你所料.

访问索引7:

>>> x[7]
Traceback (most recent call last):
  File "", line 1, in 
IndexError: list index out of range

再次,如你所料.

但是,尝试将列表从索引7切片到列表末尾:

>>> x[7:]
[]

???

解决方法是在使用列表切片时进行大量测试.我希望我只是得到一个错误.更容易调试.



9> Jason Baker..:

包中不包含__init__.py.那个人有时还会得到我.



10> David..:

我所处理的唯一问题是CPython的GIL.如果出于某种原因你期望CPython中的python线程同时运行......那么它们就不是了,而且这些已经被Python人群甚至Guido自己记录得很好.

对CPython线程的一个长期但彻底的解释以及一些在幕后发生的事情以及为什么CPython的真正并发性是不可能的. http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/


如果GIL困扰你,请查看2.6中可用的新多处理模块,以便使用单独的进程进行线程处理.http://docs.python.org/library/multiprocessing.html

11> Garth Kidd..:

James Dumay雄辩地提醒我另一个Python问题:

并非所有Python的"包含电池"都很精彩.

詹姆斯的具体例子是HTTP库:httplib,urllib,urllib2,urlparse,mimetools,和ftplib.某些功能是重复的,您期望的某些功能完全不存在,例如重定向处理.坦率地说,这太可怕了.

如果我现在必须通过HTTP抓取一些东西,我使用从Yum项目分叉的urlgrabber模块.


事实上,有一个名为urllib的模块和一个名为urllib2的模块仍然在我的皮肤下.

12> pts..:

默认情况下,浮动不会以完全精度打印(不repr):

x = 1.0 / 3
y = 0.333333333333
print x  #: 0.333333333333
print y  #: 0.333333333333
print x == y  #: False

repr 打印太多数字:

print repr(x)  #: 0.33333333333333331
print repr(y)  #: 0.33333333333300003
print x == 0.3333333333333333  #: True



13> Dawie Straus..:

无意中混合oldstyle和newstyle类可能会导致看似神秘的错误.

假设您有一个由超类A和子类B组成的简单类层次结构.当实例化B时,必须首先调用A的构造函数.下面的代码正确执行此操作:

class A(object):
    def __init__(self):
        self.a = 1

class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.b = 1

b = B()

但是如果你忘记将A设为newstyle类并将其定义为:

class A:
    def __init__(self):
        self.a = 1

你得到这个追溯:

Traceback (most recent call last):
  File "AB.py", line 11, in 
    b = B()
  File "AB.py", line 7, in __init__
    super(B, self).__init__()
TypeError: super() argument 1 must be type, not classobj

与此问题有关的另外两个问题是489269和770134



14> Sven Marnach..:
def f():
    x += 1

x = 42
f()

结果是UnboundLocalError,因为静态检测本地名称.一个不同的例子是

def f():
    print x
    x = 43

x = 42
f()



15> Anon..:

您无法使用locals()['x'] =无论如何更改本地变量值.

This works:

>>> x = 1
>>> x
1
>>> locals()['x'] = 2
>>> x
2

BUT:

>>> def test():
...     x = 1
...     print x
...     locals()['x'] = 2
...     print x  # *** prints 1, not 2 ***
...
>>> test()
1
1

这实际上让我在SO的答案上烧了,因为我在一个函数之外测试了它,并得到了我想要的改变.之后,我发现它提到并与"Dive Into Python"中的globals()的情况形成对比.见例8.12.(虽然它没有注意到,如上所示,通过locals()的更改将在顶层工作.)

推荐阅读
wangtao
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有