从Python中的相对路径导入:奇怪的行为

哈里乌斯

据我从了解Python文档from package import x声明应只约束x,而不是package到当前的命名空间。但在实践中,如果package是一个相对的名字,它有时也必然!

让我举一个例子。考虑以下文件层次结构:

root/
  package/
    __init__.py
    subpackage/
      __init__.py

子包/__init__.py:

foo = 42

包/__init__.py:

from os import name
from .subpackage import foo

print(globals().get('name'))
print(globals().get('os'))
print(globals().get('foo'))
print(globals().get('subpackage'))

现在让我们从root目录运行python(v2或v3)解释器并执行

>>> import package

前三行输出是可预测的:

posix
None
42

但是最后一个<module 'package.subpackage' ...>不是None而不是,这使我有些困惑。

我错过了什么吗?这是预期的行为吗?是什么原因?


在这种情况下,这种情况对我来说似乎更奇怪:

root/
  __init__.py  # Empty.
  package/
    __init__.py
  another_package/
    __init__.py

another_package / __ init__.py:

bar = 33

包/__init__.py:

from ..another_package import bar

print(globals().get('another_package'))

现在,我在根目录之外运行此命令

>>> import root.package
None  # OK.
>>> dir(root.package)
['__builtins__', ..., '__path__', 'bar']  # OK.
>>> dir(root)
['__builtins__', ..., '__path__', 'another_package', 'package']  # What?!

为什么another_package出现在dir(root)

阿什巴

重要的是,模块最多只能加载一次(除非明确地重新加载了模块)。如果将一个模块导入多个模块,则所有模块都引用同一模块对象例如:

模块M.py

bar = 10

模块A.py

import M
M.bar = 4

模块B.py

import M
M.bar = 6

所以:

>>> import M
>>> M.bar
10
>>> import A
>>> M.bar  # A is referencing the same M module object!!
4
>>> import B
>>> M.bar # B is referencing the same M module object!!
6

现在,from ..another_package import bar执行该语句时,它基本上等效于execute from root.another_package import bar由于another_package确实是root包中的模块,所以该语句成功执行,并产生以下效果(可能还有更多效果,但是为此,我们将重点放在这3个方面):

  1. root加载如果以前装(它的__init__.py运行)
  2. bar 导入到当前名称空间
  3. another_package作为属性添加到root模块对象

一些开发人员并不完全了解第1项和第3项。

回到您的问题:让我们import root.package按顺序查看执行时会发生什么

  1. root__init__.py已运行(因为root尚未加载)
  2. package__init__.py已运行(因为package尚未加载)
  3. from ..another_package import bar执行具有上述副作用的程序,最明显的是(是,对象。每个模块只有一个,还记得吗?)添加root了属性的模块对象another_package

这就解释了为什么another_package在出现rootdir

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章