在Python中,错误未在不正确包中被捕获
编辑:
好的,我成功找到了这个问题的根源,并且有了完整的代码来重现它。不过,这看起来要么是设计上的问题,要么是Python的一个bug。
首先,创建两个兄弟包:admin
和 General
,每个包里当然都要有自己的 __init__.py
文件。
在 admin
包里放一个名为 'test.py' 的文件,里面写入以下代码:
from General.test02 import run
import RunStoppedException
try:
run()
except RunStoppedException.RunStoppedException,e:
print 'right'
except Exception,e:
print 'this is what i got: %s'%type(e)
同时在 admin
包里放一个名为 'RunStoppedException.py' 的文件,里面写入以下代码:
class RunStoppedException(Exception):
def __init__(self):
Exception.__init__(self)
在 General
包里放一个名为 'test02.py' 的文件,里面写入以下代码:
import admin.RunStoppedException
def run():
raise admin.RunStoppedException.RunStoppedException()
打印出来的结果是:
this is what i got: <class 'admin.RunStoppedException.RunStoppedException'>
而应该是 right
。这个问题只发生在一个文件和异常文件在同一个目录下时,所以它们的导入方式不同。
这是设计上的问题,还是Python的bug呢?
我使用的是Python 2.6,在eclipse+pydev环境下运行。
3 个回答
我觉得可能有两个原因
你有两个不同的异常类,但名字是一样的
补充说明: 我觉得问题出在这里,因为你用两种方式导入了异常类- from RunStoppedException import RunStoppedException
- from admin.RunStoppedException import RunStoppedException
把它们统一一下,你的问题就解决了。
你可能在使用某个开发工具(IDE),它干扰了你的代码,这听起来有点奇怪,但如果你还没试过的话,可以在命令行上运行你的代码试试看。
即使第一个和第二个方法都不能解决你的问题,写一小段代码来展示这个问题,我们可以在这里运行并修复它。不过我相信你不需要这样,因为一旦你写出这样一个小的独立脚本来复现问题,你自己也会找到解决办法。
对我来说一切正常:
[/tmp] ls admin/
RunStoppedException.py __init__.py test.py
RunStoppedException.pyc __init__.pyc
[/tmp] ls General/
__init__.py __init__.pyc test02.py test02.pyc
[/tmp] python -m admin.test
right
[/tmp]
运行环境:
Python 2.6.4 Stackless 3.1b3 060516 (release26-maint, Dec 14 2009, 23:28:06)
[GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin
我猜你的路径上可能有另一个叫“General”的东西,可能是之前测试时留下的,这就是为什么出现的错误不匹配的原因。你有没有尝试过用 id
或 inspect.getabsfile
来调试一下?如果试过,结果是什么呢?
import admin.RunStoppedException
这是一个模糊的相对导入。你是想从顶层模块 admin
导入 RunStoppedException
吗?还是在一个包内从 mypackage.admin
导入?如果你当前的工作目录(也就是 Python 查找模块的地方)恰好在这个包里面,那么这两者都有可能,具体取决于你是怎么运行这个脚本的,Python 是否知道它在一个包里面。
如果你在不同的模块中同时使用了 import admin.RunStoppedException
和 import RunStoppedException
,那么可能会导入同一个模块的两个副本:一个是顶层的 RunStoppedException
,另一个是包 admin
的子模块 admin.RunStoppedException
。这样就会导致两个异常实例,后面在 except
处理时就会出现不匹配的问题。
所以不要使用隐式相对导入。无论如何,这种方式都要被淘汰(可以参考 PEP328)。总是要写出完整的模块名称,比如 import mypackage.admin.RunStoppedException
。不过要避免将模块名和类名用同样的名称,这样会让人很困惑。需要注意的是,Python 允许你这样写:
except RunStoppedException:
这里的标识符指的是一个模块,而不是异常的子类。这是历史原因造成的,可能将来也会被改变,但目前这样做可能会隐藏一些错误。一个常见的做法是使用 mypackage.exceptions
来存放多个异常。每个类放一个文件是 Java 的习惯,但在 Python 中并不被推荐。
一般来说,尽量减少导入模块内容(比如类)也是个好主意。如果某个地方修改了模块内的 RunStoppedException
的副本,你在不同的脚本中就会有不同的副本。虽然类通常不会改变,但模块级的变量可能会,而当你把东西从它所属的模块外部使用时,猴子补丁和重新加载就会变得更加困难。