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

时间:2012-09-24 02:13:14

标签: python

main.py:

    import package

包/ __ 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()

包/ foo.py:

    print 'Hello from foo'

执行main.py将输出如下:

['__builtins__', '__file__', '__package__', '__path__', '__name__', 'do_import', '__doc__']
[]
Hello from foo
['mod']
['__builtins__', '__file__', '__package__', '__path__', '__name__', 'foo', 'do_import', '__doc__']

import中的__init__.py无法正常工作。 请注意,全局命名空间有一个'foo',它应该只绑定到本地'mod'

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

这怎么可能发生?

1 个答案:

答案 0 :(得分:3)

啊!整蛊,但我知道了!

“foo”不是一个简单的“其他包” - 它似乎是Python作为“包”模块的子模块。

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

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

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

- 因为这可能发生在您拥有的实际代码中,并且相当于“do_import”是从包外部的代码调用的(并且具有使您的子模块出现在包的全局命名空间中的副作用),所以不容易解决你正在做的事情。我的建议是在子模块名称的开头添加下划线(_),如果它们不打算从包外部的通用代码中调用。 (如果在这种情况下有人from package import *

,它也不会出现