Python 导入被标准库覆盖(Python 2.4)

6 投票
3 回答
2294 浏览
提问于 2025-04-17 08:37

我正在写一个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 个回答

-1

文件层级结构

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

0

从项目目录(也就是包含package目录的上级目录)开始:

python -mpackage.state # Python 2.6+
python2.4 -c'from package.state import test; test()'
  • 在你的模块中总是使用绝对导入,也就是说要用import package.signal,而不是import signal,除非你非常清楚自己在做什么。
  • 确保package的上级目录在sys.path中。像unittest2nosepytest这些测试工具可能会帮你处理这个问题。
  • 避免使用和标准库模块同名的模块名。很多测试工具和代码分析工具会出问题,它们会把正在处理的模块所在的目录添加到sys.path中。
  • 运行python package/stat.py会把package添加到sys.path中(具体可以查看这个链接)。在这种情况下,你是不希望这样做的。
6

这个问题的原因在于,内置的 signal 模块在解释器启动时就已经被导入了。所以当你的 module.py 代码运行时,sys.modules 中已经有一个名为 signal 的条目,它的值是内置模块。因为 sys.modulesimport 语句首先查找的地方,所以它会直接返回那个内置模块,而不会去找你自己写的 signal.py

我能想到三种解决办法:

  1. 重命名你的模块。这是最简单的解决方案。你不需要担心你的模块名和已安装的包(比如 eggs)冲突,因为 通常 import 语句会按照 sys.path 指定的顺序搜索目录,而你运行解释器时的当前工作目录是列表中的第一位。signal 是个特殊情况,因为它在你的代码运行之前就会自动导入,但这种情况的模块数量有限,你只需要避免和这些模块名字冲突。
  2. 你可以使用 del sys.modules['signal'] 来删除这个条目,然后用你自己的 signal 模块替换它。但不要真的这样做,除非你的模块确实是为了替代内置的 signal。如果你这么做了,那么从那时起,任何运行 import signal 的代码都会得到 的版本,而不是内置的版本,这可能会对任何需要 signal 模块正常工作的内部代码造成麻烦。
  3. 你可以使用 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 中,也不会在搜索路径之前查找内置模块。

撰写回答