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

时间:2015-02-28 19:48:41

标签: python namespaces python-import

据我所知python docsfrom 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)

1 个答案:

答案 0 :(得分:1)

实现模块最多加载一次非常重要(除非它们明确地reloaded)。如果在多个模块中导入模块,则所有模块都引用相同的模块 object 。 E.g:

模块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时,它基本上等同于执行from root.another_package import bar。由于another_package确实是root包中的一个模块,因此该语句会成功,并产生以下效果(可能会有更多,但出于此目的,请关注这些3):< / p>

  1. root已装入,如果之前未加载(已运行&#39; __init__.py
  2. bar已导入当前命名空间
  3. another_package作为属性添加到root模块对象
  4. 有些开发人员并不完全了解第1项和第3项。

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

    1. root&#39; __init__.py已投放(因为root尚未加载)
    2. package&#39; __init__.py已投放(因为package尚未加载)
    3. from ..another_package import bar被执行,它具有上面提到的副作用,最值得注意的是(是的。对象。每个模块只有一个,记得吗?)root的模块对象具有属性another_package已添加到其中。
    4. 这解释了another_package root中出现dir的原因。