Python 程序正在导入自身

Python program importing itself

我创建了一个名为 challenge.py 的文件,其中包含以下代码:

import challenge

def main():
     print('Circular much...')

challenge.main()

从这里我期待 python 由于循环导入导入文件 运行 而引发错误,但我发现在 python 3.7 和 3.8 上这个文件运行并打印出 Circular much... 两次。我会理解一次,因为这意味着文件本身导入时的其余部分不是 运行 我会理解递归错误,因为它 运行 challenge.main() 无限向下堆栈,但我不明白为什么它打印两次并停止?

通过以下方式追踪:

import challenge

好的,我们 import challenge.py。我们开始...

    import challenge

我们已经在导入 challenge.py,所以

    def main():
         print('Circular much...')

在命名空间 challenge 中定义了函数 main()。酷

    challenge.main()

现在调用 命名空间challenge 中的函数main()。打印 Circular much...。这是您的第一张印刷品。

现在我们又回到了主模块。

def main():
     print('Circular much...')

这定义了全局命名空间中的函数 main()(永远不会被调用)。

challenge.main()

这会调用命名空间 challenge 中的函数 main(),再次打印 Circular much....

我们完成了。将您的留言打印两份。

有帮助吗?

看看 sys.modules 可能会有启发。

例如:

import sys

def check_modules():
     print("seeing what we have...")
     if "challenge" in sys.modules:

          module = sys.modules["challenge"]
          
          print(f"sys.modules contains {module.__file__}")
          
          if hasattr(module, "challenge"):
               print("... and it contains the variable 'challenge'")
               if module is module.challenge:
                    print("... pointing back at itself")
                    
          if hasattr(module, "main"):
               print("... and it contains the variable 'main'")
               
     print()

print("Before import:")
check_modules()

import challenge

print("After import:")
check_modules()

def main():
     print('Circular much...')

print("After declaring 'main':")
check_modules()
     
challenge.main()

给出:

Before import:
seeing what we have...

Before import:
seeing what we have...
sys.modules contains /tmp/challenge.py

After import:
seeing what we have...
sys.modules contains /tmp/challenge.py
... and it contains the variable 'challenge'
... pointing back at itself

After declaring 'main':
seeing what we have...
sys.modules contains /tmp/challenge.py
... and it contains the variable 'challenge'
... pointing back at itself
... and it contains the variable 'main'

Circular much...
After import:
seeing what we have...
sys.modules contains /tmp/challenge.py
... and it contains the variable 'challenge'
... pointing back at itself
... and it contains the variable 'main'

After declaring 'main':
seeing what we have...
sys.modules contains /tmp/challenge.py
... and it contains the variable 'challenge'
... pointing back at itself
... and it contains the variable 'main'

Circular much...

如您所见,模块在导入的 开始 被添加到 sys.modules,在导入模块中的实际代码是 运行.如果在 sys.modules 中存在模块文件时到达 import 语句,那么这足以防止它被再次导入,这就是为什么只有一层递归。

导入完成后,将导入的结果(一个模块对象)赋值给变量challenge,代码中的if module is module.challenge测试确认是对的引用与创建名称的模块相同的模块(因为导入已导入的模块只是重复使用已创建的相同模块对象)。

现在关于如何调用 challenge.main 的问题:正是因为 challenge 只是对当前模块的引用,这意味着当函数定义被执行时,从而创建当前模块对象中的名称 main,它指向的同一个函数对象同样可以作为 challenge.main 而不是 main.

来访问

I was expecting python to raise an error due to the circular import of importing the file which is running

既然人们已经解释了它 没有 的原因,让我们考虑一下如何 使 起作用。也许:

from sys import argv

print('Circular much...')

exec(open(argv[0]).read())

输出

% python3 challenge.py
Circular much...
Circular much...
Circular much...
Circular much...
Circular much...
... # 489 more recursions later
Circular much...
Circular much...
Circular much...
Circular much...
Circular much...
Traceback (most recent call last):
  File "challenge.py", line 5, in <module>
    exec(open(argv[0]).read())
  File "<string>", line 5, in <module>
  File "<string>", line 5, in <module>
  File "<string>", line 5, in <module>
  [Previous line repeated 494 more times]
  File "<string>", line 3, in <module>
RecursionError: maximum recursion depth exceeded while calling a Python object
% 

只有 运行 两次:一次是因为文件是 运行,另一次是因为文件是导入的。第二次读取行 'import challenge' 时,它不会永远递归导入,因为它已经导入到此命名空间中,这会阻止它进行第二次导入。就像在同一个脚本中两次导入模块只会导入一次模块一样。