下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922
论坛 >编程语言 >Python程序员的10个常见错误 (2)

Python程序员的10个常见错误 (2)

希尔瓦娜斯发布于 2018-01-23 10:42查看:592回复:1

常见错误6:搞不清楚在闭包(closures)中Python是怎样绑定变量的

看这个例子:

image.png

期望得到下面的输出:

image.png

但是实际上得到的是:

image.png

意外吧!

这是由于Python的后期绑定(late binding)机制导致的,这是指在闭包中使用的变量的值,是在内层函数被调用的时候查找的。因此在上面的代码中,当任一返回函数被调用的时候,i的值是在它被调用时的周围作用域中查找(到那时,循环已经结束了,所以i已经被赋予了它最终的值4)。

解决的办法比较巧妙:

image.png

这下对了!这里利用了默认参数去产生匿名函数以达到期望的效果。有人会说这很优美,有人会说这很微妙,也有人会觉得反感。但是如果你是一名Python程序员,重要的是能理解任何的情况。

常见错误7:循环加载模块

假设你有两个文件,a.py和b.py,在这两个文件中互相加载对方,例如:

在a.py中:

image.png

首先,我们试着加载a.py:

image.png

没有问题。也许让人吃惊,毕竟有个感觉应该是问题的循环加载在这儿。

事实上在Python中仅仅是表面上的出现循环加载并不是什么问题。如果一个模块以及被加载了,Python不会傻到再去重新加载一遍。但是,当每个模块都想要互相访问定义在对方里的函数或者变量时,问题就来了。

让我们再回到之前的例子,当我们加载a.py时,它再加载b.py不会有问题,因为在加载b.py,它并不需要访问a.py的任何东西,而在b.py中唯一的引用就是调用a.f()。但是这个调用是在函数g()中完成的,并且a.py或者b.py中没有人调用g(),所以这会儿心情还是美丽的。

但是当我们试图加载b.py时(之前没有加载a.py),会发生什么呢:

image.png

恭喜你,出错了。这里问题出在加载b.py的过程中,Python试图加载a.py,并且在a.py中需要调用到f(),而函数f()又要访问到b.x,但是这个时候b.x却还没有被定义。这就产生了AttributeError异常。

解决的方案可以做一点细微的改动。改一下b.py,使得它在g()里面加载a.py:

image.png

这会儿当我们加载b.py的时候,一切安好:

image.png


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

Python的一个优秀的地方在于它提供了丰富的库模块。但是这样的结果是,如果你不下意识的避免,很容易你会遇到你自己的模块的名字与某个随Python附带的标准库的名字冲突的情况(比如,你的代码中可能有一个叫做email.py的模块,它就会与标准库中同名的模块冲突)。

这会导致一些很粗糙的问题,例如当你想加载某个库,这个库需要加载Python标准库里的某个模块,结果呢,因为你有一个与标准库里的模块同名的模块,这个包错误的将你的模块加载了进去,而不是加载Python标准库里的那个模块。这样一来就会有麻烦了。

所以在给模块起名字的时候要小心了,得避免与Python标准库中的模块重名。相比起你提交一个“Python改进建议(Python Enhancement Proposal (PEP))”去向上要求改一个标准库里包的名字,并得到批准来说,你把自己的那个模块重新改个名字要简单得多。

常见错误9:不能区分Python 2和Python 3

看下面这个文件foo.py:


image.png

在Python 2里,运行起来没有问题:

image.png

但是如果拿到Python 3上面玩玩:

image.png

这是怎么回事?“问题”在于,在Python 3里,在except块的作用域以外,异常对象(exception object)是不能被访问的。(原因在于,如果不这样的话,Python会在内存的堆栈里保持一个引用链直到Python的垃圾处理将这些引用从内存中清除掉。更多的技术细节可以参考这里。)

避免这样的问题可以这样做:保持在execpt块作用域以外对异常对象的引用,这样是可以访问的。下面是用这个办法对之前的例子做的改动,这样在Python 2和Python 3里面都运行都没有问题。


image.png

在Py3里面运行:

image.png


耶!

(顺带提一下,我们的“Python招聘指南”里讨论了从Python 2移植代码到Python 3时需要注意的其他重要的不同之处。)

常见错误10:错误的使用__del__方法

假设有一个文件mod.py中这样使用:

image.png

然后试图在another_mod.py里这样:

image.png

那么你会得到一个恶心的AttributeError异常。

为啥呢?这是因为(参考这里),当解释器关闭时,模块所有的全局变量会被置为空(None)。结果便如上例所示,当__del__被调用时,名字foo已经被置为空了。

使用atexit.register()可以解决这个问题。如此,当你的程序结束的时候(退出的时候),你的注册的处理程序会在解释器关闭之前处理。

这样理解的话,对上面的mod.py可以做如下的修改:

image.png

这样的实现方式为在程序正常终止时调用清除功能提供了一种干净可靠的办法。显然,需要foo.cleanup决定怎么处理绑定在self.myhandle上的对象,但你知道怎么做的。


收藏(0)0
查看评分情况

全部评分

此主贴暂时没有点赞评分

总计:0

回复分享

版主推荐

    共有1条评论

    • IT宅男
    • mr jack
    • Mr ken
    • Mright
    • cappuccino
    • YUI
    • 课课家运营团队
    • 课课家技术团队1
    • 酸酸~甜甜
    • 选择版块:

    • 标题:

    • 内容

    • 验证码:

    • 标题:

    • 内容

    • 选择版块:

    移动帖子x

    移动到: