在包的__init__.py中导入子模块时出现奇怪的命名空间污染

you*_*ufu 8 python

main.py:

    import package
Run Code Online (Sandbox Code Playgroud)

包/ __ init__.py:

    # use function to split local and global namespace
    def do_import():
        print globals().keys()
        print locals().keys()

        import foo as mod

        print locals().keys()
        print globals().keys()

    do_import()
Run Code Online (Sandbox Code Playgroud)

包/ foo.py:

    print 'Hello from foo'
Run Code Online (Sandbox Code Playgroud)

执行main.py将输出如下:

['__builtins__', '__file__', '__package__', '__path__', '__name__', 'do_import', '__doc__']
[]
Hello from foo
['mod']
['__builtins__', '__file__', '__package__', '__path__', '__name__', 'foo', 'do_import', '__doc__']
Run Code Online (Sandbox Code Playgroud)

import__init__.py没有按预期工作.请注意,全局命名空间有一个'foo',它只能绑定到本地'mod'

即使是 exec "import foo as mod" in {'__name__': __name__, '__path__': __path__} 无法阻止全局命名空间被修改

怎么会发生这种情况?

jsb*_*eno 4

啊! 很棘手,但我明白了!

“foo”不是一个简单的“其他包”——Python 认为它是“包”模块的子模块。

当您第一次运行“package”时 - 从外部脚本导入它,或者使用-m命令行开关运行它(但如果python package/__init__.py直接从命令行运行则不会),“package”模块将被解析,并添加到sys.modules字典(在sys模块上)。

foo读取子模块时,除了直接放在sys.moduleskey 下外["package.foo"],还会将其设置为其父模块的属性。因此,它可以在您的 Python 应用程序中作为package.foo. 发生的情况是,在 , 中设置属性与在运行时在全局变量sys.modules["package"]中设置键具有相同的效果。package/__init__.py这就是正在发生的事情。

我希望我能将这个过程正确地翻译成文字 - 如果没有,请通过评论再次询问。

-- 由于这可能发生在您拥有的真实代码中,并且从包外部的代码调用“do_import”的等效项(并且具有使子模块出现在包的全局命名空间上的副作用),因此您所做的事情并不容易解决。_我的建议是,如果不打算从包外部的通用代码调用子模块名称,则只需在子模块名称的开头添加下划线 ( ) 即可。from package import *(如果有人在这种情况下这样做,它也不会显示)