为什么循环导入会使用`isinstance`导致对象标识出现问题?

Gra*_*ham 12 python python-3.x

在隔离一个bug几个小时后,我想出了以下MCVE示例来演示我遇到的问题:

a.py:

from b import get_foo_indirectly

class Foo:
    pass

if __name__ == '__main__':
    print("Indirect:", isinstance(get_foo_indirectly(), Foo))
    print("Direct:", isinstance(Foo(), Foo))
Run Code Online (Sandbox Code Playgroud)

b.py:

def get_foo_indirectly():
    from a import Foo
    return Foo()
Run Code Online (Sandbox Code Playgroud)

a.py的预期输出是:

Indirect: True
Direct: True
Run Code Online (Sandbox Code Playgroud)

实际输出是:

Indirect: False
Direct: True
Run Code Online (Sandbox Code Playgroud)

此外,如果我创建一个单独的模块c.py,输出是按预期的:

from a import Foo
from b import get_foo_indirectly

if __name__ == '__main__':
    print("Indirect:", isinstance(get_foo_indirectly(), Foo))
    print("Direct:", isinstance(Foo(), Foo))
Run Code Online (Sandbox Code Playgroud)

显然,isinstance进口机械之间的相互作用并不像我预期的那样.好像使用循环进口让我很难受.为什么?这是Python的预期行为吗?

请注意,这对我遇到此行为的实际上下文非常简单; 模块a和b都是大模块,而b是分开的,因为它有一个与a不同的目的.现在我已经看到了循环进口的后果,我可能会将它们结合起来,也许会降低b中一些冗长的行为.

Gab*_*iel 8

运行Python脚本时,它会自动采用名称__main__.当你a.pyb.pyPython中导入假定通常的模块名称(即文件名)时,在运行时Python改为,__main__因为它是入口点脚本; 所以,它就像Foo在两个不同的地方声明了类:__main__模块和a模块.

然后,您将比较a.Foo(在内部创建get_foo_indirectly)和__main__.Foo.

这已经在这里讨论.

如果需要进行循环导入,请不要将入口点脚本放在循环中.通过这种方式,您可以避免Python的这种行为 - 非常有用 - .