Python 导入被标准库覆盖(Python 2.4)
我正在写一个Python包,但遇到了一个问题,就是因为名字冲突,导致我自己的文件没有被导入,而是导入了标准库。
比如,下面是我的文件结构:
package/__init__.py
# No data in this file
package/module.py
#!/usr/bin/env python
print 'Loading module.py'
import signal
package/signal.py
#!/usr/bin/env python
print 'Loading signal.py'
当我运行这个时,得到的结果是:
$ ./module.py
Loading module.py
我希望得到的是:
$ ./module.py
Loading module.py
Loading signal.py
实际的问题:
所以,当我运行module.py时,它的import signal
却导入了标准库里的版本。我该怎么做才能让module.py导入我自己的signal.py呢?
正如标签中提到的,这个需要在python-2.4.3上运行。虽然这个版本比较旧,但它是在RHEL 5中包含的。
一些额外的信息
为了提供更多信息,我的设置是这样的:
[10:30pm][~/test] tree .
.
|-- package
| |-- __init__.py
| |-- module.py
| `-- signal.py
`-- script
[10:30pm][~/test] cat script
#!/usr/bin/env python
from package import signal
[10:30pm][~/test] cat package/__init__.py
[10:30pm][~/test] cat package/module.py
#!/usr/bin/env python
print "Loading module.py"
import signal
[10:30pm][~/test] cat package/signal.py
#!/usr/bin/env python
print "Loading signal.py"
[10:30pm][~/test] python ./script
Loading signal.py
[10:32pm][~/test] python ./package/module.py
Loading module.py
[10:32pm][~/test] python -m package.module
python: module package.module not found
请注意,当我运行./package/module.py时,./package/signal.py中的打印语句没有被触发。这意味着加载的signal是来自标准库的。
3 个回答
文件层级结构
.
|-- package
| |-- __init__.py
| |-- module.py
| `-- signal.py
`-- script
`-- __init__.py
script.py
#!/usr/bin/env python
from package import module
package/module.py
#!/usr/bin/env python
print 'Loading module.py'
from package import signal
package/signal.py
#!/usr/bin/env python
print 'Loading signal.py'
输出:
$ ./script
Loading module.py
Loading signal.py
编辑
刚刚在python 2.4.3上测试了这个,修改了module.py。
从项目目录(也就是包含package
目录的上级目录)开始:
python -mpackage.state # Python 2.6+
python2.4 -c'from package.state import test; test()'
- 在你的模块中总是使用绝对导入,也就是说要用
import package.signal
,而不是import signal
,除非你非常清楚自己在做什么。 - 确保
package
的上级目录在sys.path
中。像unittest2
、nose
和pytest
这些测试工具可能会帮你处理这个问题。 - 避免使用和标准库模块同名的模块名。很多测试工具和代码分析工具会出问题,它们会把正在处理的模块所在的目录添加到
sys.path
中。 - 运行
python package/stat.py
会把package
添加到sys.path
中(具体可以查看这个链接)。在这种情况下,你是不希望这样做的。
这个问题的原因在于,内置的 signal
模块在解释器启动时就已经被导入了。所以当你的 module.py
代码运行时,sys.modules
中已经有一个名为 signal
的条目,它的值是内置模块。因为 sys.modules
是 import
语句首先查找的地方,所以它会直接返回那个内置模块,而不会去找你自己写的 signal.py
。
我能想到三种解决办法:
- 重命名你的模块。这是最简单的解决方案。你不需要担心你的模块名和已安装的包(比如 eggs)冲突,因为 通常
import
语句会按照sys.path
指定的顺序搜索目录,而你运行解释器时的当前工作目录是列表中的第一位。signal
是个特殊情况,因为它在你的代码运行之前就会自动导入,但这种情况的模块数量有限,你只需要避免和这些模块名字冲突。 - 你可以使用
del sys.modules['signal']
来删除这个条目,然后用你自己的signal
模块替换它。但不要真的这样做,除非你的模块确实是为了替代内置的signal
。如果你这么做了,那么从那时起,任何运行import signal
的代码都会得到 你 的版本,而不是内置的版本,这可能会对任何需要signal
模块正常工作的内部代码造成麻烦。 你可以使用
imp
模块 来访问import
函数的内部机制,或者至少是等效的代码,这样可以绕过对sys.modules
的检查。如果你确实需要这样做,这里有一段代码示例可以参考:
import imp, sys f, path, desc = imp.find_module('signal', sys.path) if f: f.close() signal = imp.new_module('signal') execfile(path, signal.__dict__) else: raise ImportError('signal.py not found') # signal is your module
这段代码的功能大致等同于
import signal
,但它不会将找到的模块插入到sys.modules
中,也不会在搜索路径之前查找内置模块。