模块被重复导入。Python解释器可能存在漏洞

1 投票
2 回答
1102 浏览
提问于 2025-04-17 09:04

我在我的一个项目中做了一个比较复杂的导入方案,觉得可能发现了Python解释器的一个bug,这个bug会导致模块被导入两次。

下面是我的测试项目的结构:

  • /

    • Launcher.bat — 从这里运行项目。它使用Python 3.2的可执行文件来启动'main/__init__.py'

    • main/__init__.py — 这是__main__脚本,也就是'Launcher.bat'启动的那个脚本

    • main/foo.py — 里面有一个空的类

    • external/__init__.py — 这是一个外部脚本,用来演示问题

./Launcher.bat

@echo off
C:\Python32\python.exe main\__init__.py
pause

./main/__init__.py

from foo import Foo
print("In 'main', Foo has id:", id(Foo))

# Add the directory from which 'Launcher.bat' was run,
# which is not the same as the './main' directory
# in which this script is located
import sys
sys.path.insert(1, '.')


# This script will try to import class Foo, but in doing so
# will casue the interpreter to import this './main/__init__.py'
# script a second time.
__import__('external')

./main/foo.py

class Foo:
    pass

./external/__init__.py

from main.foo import Foo
print("In 'external', Foo has id:", id(Foo))

这些代码会打印出'主脚本被导入'的信息两次。如果外部脚本导入了其他脚本,那些脚本也会被导入两次。我只在Python 3.2上测试过这个。请问这是个bug,还是我做错了什么?

程序的输出是:

在'main'中,Foo的ID是:12955136
在'main'中,Foo的ID是:12955136
在'external'中,Foo的ID是:12957456
按任意键继续 . . .

2 个回答

2

我觉得这不是个错误。你应该去 python-dev 的讨论组问一下,那里能得到更权威的答案。你运行脚本的时候执行了一次,然后从外部导入的时候又执行了一次,所以那行代码被打印了两次。其实并不是导入了两次。

不过,这样的设置真是糟糕透了。这里有很多不符合规范的地方。虽然有些是为了演示目的,但整体看起来还是很乱。

  1. 你不应该把包的 __init__.py 文件当成一个需要运行的文件。主要的入口应该是一个导入这个包的脚本。
  2. 你不应该让一个被导入的模块去导入它所依赖的模块,就像你外部模块对主模块所做的那样。
1

第一个print可能会让人误解:因为你不是在导入,而是在第一次执行这个文件时(__name__ == '__main__'为真),主模块只会被导入一次。你可以把起始点放到一个单独的文件里,或者检查一下__name__ == '__main__'

顺便说一下,循环 导入是个坏主意。你应该解决循环导入的问题(可以把foo移动到一个专门的库里)。另外,你也可以让你的模块可重入(也就是说,在添加当前目录之前,检查一下它是否在sys.path里)。

撰写回答