[英]Python import module sharing name with a function in __init__.py
My tree looks like我的树看起来像
parent/
|--__init__.py
\--a.py
And the content of __init__.py
is而
__init__.py
的内容是
import parent.a as _a
a = 'some string'
When I open up a Python at the top level and import parent.a
, I would get the string instead of module.当我在顶层打开 Python 并
import parent.a
,我会得到字符串而不是模块。 For example import parent.a as the_a; type(the_a) == str
例如
import parent.a as the_a; type(the_a) == str
import parent.a as the_a; type(the_a) == str
. import parent.a as the_a; type(the_a) == str
。
So I think OK probably import
is importing the name from the parent
namespace, and it's now overridden.所以我认为 OK 可能
import
是从parent
命名空间导入名称,现在它被覆盖了。 So I figure I can go import parent._a as a_module
.所以我想我可以
import parent._a as a_module
。 But this doesn't work as there is "No module named _a".但这不起作用,因为“没有名为 _a 的模块”。
This is very confusing.这是非常令人困惑的。 A function can override a module with the same name, but a module cannot take on a new name and "reexport".
函数可以覆盖具有相同名称的模块,但模块不能采用新名称和“重新导出”。
Is there any explanation I'm not aware of?有什么我不知道的解释吗? Or is this documented feature?
或者这是记录在案的功能?
Even more confusing, if I remove the import
statement in __init__.py
, everything is back normal again ( import parent.a; type(parent.a) is module
).更令人困惑的是,如果我删除
__init__.py
中的import
语句,一切import parent.a; type(parent.a) is module
恢复正常( import parent.a; type(parent.a) is module
)。 But why is this different?但为什么这不同呢? The
a
name in parent
namespace is still a string. parent
命名空间中的a
名称仍然是一个字符串。
(I ran on Python 3.5.3 and 2.7.13 with the same results) (我在 Python 3.5.3 和 2.7.13 上运行,结果相同)
In an import
statement, the module reference never uses attribute lookups.在
import
语句中,模块引用从不使用属性查找。 The statements声明
import parent.a # as ...
and和
from parent.a import ... # as ...
will always look for parent.a
in the sys.modules
namespace before trying to further initiate module loading from disk.在尝试从磁盘进一步启动模块加载之前,将始终在
sys.modules
命名空间中查找parent.a
。
However, for from ... import name
statements, Python does look at attributes of the resolved module to find name
, before looking for submodules.但是,对于
from ... import name
语句,Python 在查找子模块之前确实会查看解析模块的属性以查找name
。
Module globals and the attributes on a module object are the same thing.模块全局变量和模块对象上的属性是一回事。 On import, Python adds submodules as attributes (so globals) to the parent module, but you are free to overwrite those attributes, as you did in your code.
在导入时,Python 将子模块作为属性(全局变量)添加到父模块,但您可以像在代码中一样自由地覆盖这些属性。 However, when you then use an import with the
parent.a
module path, attributes do not come into play.但是,当您使用带有
parent.a
模块路径的导入时,属性不会起作用。
From the Submodules section of the Python import system reference documentation :从Python 导入系统参考文档的 子模块部分:
When a submodule is loaded using any mechanism [...] a binding is placed in the parent module's namespace to the submodule object.
当使用任何机制加载子模块时 [...] 将绑定放置在父模块的命名空间中到子模块对象。 For example, if package
spam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule.例如,如果包
spam
有一个子模块foo
,在导入spam.foo
,spam
将有一个属性foo
绑定到子模块。
Your import parent.a as _a
statement adds two names to the parent
namespace;您的
import parent.a as _a
语句将两个名称添加到parent
名称空间; first a
is added pointing to the parent.a
submodule, and then _a
is also set, pointing to the same object.第一
a
被加到指向parent.a
子模块,然后_a
也被设定,指向同一个对象。
Your next line replaces the name a
with a binding to the 'some string'
object.您的下一行将名称
a
替换为'some string'
对象的绑定。
The Searching section of the same details how Python goes about finding a module when you import:相同的 搜索部分详细介绍了 Python 在导入时如何查找模块:
To begin the search, Python needs the fully qualified name of the module [...] being imported.
要开始搜索,Python 需要导入的模块 [...] 的完全限定名称。
[...]
[...]
This name will be used in various phases of the import search, and it may be the dotted path to a submodule, eg
foo.bar.baz
.此名称将用于导入搜索的各个阶段,它可能是子模块的虚线路径,例如
foo.bar.baz
。 In this case, Python first tries to importfoo
, thenfoo.bar
, and finallyfoo.bar.baz
.在这种情况下,Python 首先尝试导入
foo
,然后是foo.bar
,最后是foo.bar.baz
。 If any of the intermediate imports fail, aModuleNotFoundError
is raised.如果任何中间导入失败,
ModuleNotFoundError
引发ModuleNotFoundError
。
then further on然后进一步
The first place checked during import search is
sys.modules
.在导入搜索期间检查的第一个位置是
sys.modules
。 This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths.此映射用作之前导入的所有模块的缓存,包括中间路径。 So if
foo.bar.baz
was previously imported,sys.modules
will contain entries forfoo
,foo.bar
, andfoo.bar.baz
.因此,如果之前导入了
foo.bar.baz
,则sys.modules
将包含foo
、foo.bar
和foo.bar.baz
条目。 Each key will have as its value the corresponding module object.每个键都有相应的模块对象作为其值。
During import, the module name is looked up in
sys.modules
and if present, the associated value is the module satisfying the import, and the process completes.在导入期间,在
sys.modules
查找模块名称,如果存在,则关联的值是满足导入的模块,过程完成。 [...] If the module name is missing, Python will continue searching for the module.[...] 如果缺少模块名称,Python 将继续搜索该模块。
So when trying to import parent.a
all that matters is that sys.modules['parent.a']
exists.因此,当尝试导入
parent.a
,重要的是sys.modules['parent.a']
存在。 sys.modules['parent'].a
is not consulted. sys.modules['parent'].a
没有被咨询。
Only from module import ...
would ever look at attributes.只有
from module import ...
才会查看属性。 From the import
statement documentation :从
import
语句文档:
The
from
form uses a slightly more complex process:from
表单使用了一个稍微复杂的过程:
- find the module specified in the from clause, loading and initializing it if necessary;
找到 from 子句中指定的模块,必要时加载和初始化它;
- for each of the identifiers specified in the import clauses:
对于 import 子句中指定的每个标识符:
- check if the imported module has an attribute by that name
检查导入的模块是否具有该名称的属性
- if not, attempt to import a submodule with that name and then check the imported module again for that attribute
如果没有,请尝试导入具有该名称的子模块,然后再次检查导入的模块的该属性
- [...]
[...]
So from parent import _a
would work, as would from parent import a
, and you'd get the parent.a
submodule and the 'some string'
object, respectively.所以
from parent import _a
会起作用,就像from parent import a
,你会分别得到parent.a
子模块和'some string'
对象。
Note that sys.modules
is writable, if you must have import parent._a
work, you can always just alter sys.modules
directly:请注意
sys.modules
是可写的,如果你必须让import parent._a
工作,你总是可以直接改变sys.modules
:
sys.modules['parent._a'] = sys.modules['parent.a'] # make parent._a an alias for parent.a
import parent._a # works now
I think I have a coherent understanding of this problem now, just documenting my findings in case others run into this.我想我现在对这个问题有一个连贯的理解,只是记录我的发现以防其他人遇到这个问题。
What Martijn said above is mostly true, expanding on that answer, import parent.a as _a
is a two step process. Martijn 上面所说的大部分是正确的,扩展了那个答案,
import parent.a as _a
是一个两步过程。 The first step is module lookup of parent.a
, which never goes through attribute lookup, and then it does a binding onto sys.modules
, and then an attribute binding of the module to attribute a
in parent
.第一步是
parent.a
模块查找,它从不通过属性查找,然后它绑定到sys.modules
,然后将模块的属性绑定到parent
属性a
。 In fact this is all you get if you only use import parent.a
.事实上,如果你只使用
import parent.a
这就是你所得到的。 This part is described thoroughly by the previous answer.这部分在前面的回答中有详细的描述。
The second part as _a
does an attribute lookup of parent.a
, and binds it onto the name _a
. as _a
的第二部分执行parent.a
的属性查找,并将其绑定到名称_a
。 So to answer my original question, now if I go outside and start an interactive Python interpreter, now parent.a
has been overwritten to the string in __init__.py
, and import parent.a as the_a; the_a
所以为了回答我最初的问题,现在如果我出去启动一个交互式 Python 解释器,现在
parent.a
已被覆盖为__init__.py
的字符串,并将import parent.a as the_a; the_a
import parent.a as the_a; the_a
would get me the string. import parent.a as the_a; the_a
会给我字符串。 In fact, this is the same as import parent.a; parent.a
其实这和
import parent.a; parent.a
是一样的import parent.a; parent.a
import parent.a; parent.a
. import parent.a; parent.a
。 Both the_a
and parent.a
are the results of attribute lookup. the_a
和parent.a
都是属性查找的结果。 I could still get the submodule by parent._a
or sys.modules["parent.a"]
.我仍然可以通过
parent._a
或sys.modules["parent.a"]
获取子模块。
To answer my follow up question:回答我的后续问题:
Even more confusing, if I remove the
import
statement in__init__.py
, everything is back normal again (import parent.a; type(parent.a)
is module).更令人困惑的是,如果我删除
__init__.py
中的import
语句,一切import parent.a; type(parent.a)
恢复正常(import parent.a; type(parent.a)
is module )。 But why is this different?但为什么这不同呢? The
a
name inparent
namespace is still a string.parent
命名空间中的a
名称仍然是一个字符串。
This is when I import parent.a
in the outside interactive Python interpreter, it first evaluates __init__.py
, which does the overwriting of parent.a
to a string.这是当我在外部交互式 Python 解释器中
import parent.a
时,它首先评估__init__.py
,它将parent.a
覆盖为字符串。 But the import hasn't finished yet, it goes on importing the submodule parent.a
, and since we are still in the importing part, we don't do attribute lookups, and so we find the correct submodule.但是导入还没有完成,它继续导入子模块
parent.a
,由于我们还在导入部分,我们不做属性查找,所以我们找到了正确的子模块。 When all this is done, it binds the submodule to a
of parent
, thus overwriting the string that was overwriting the submodule, and making it all correct again.当这一切都完成后,它结合了子模块
a
的parent
,从而覆盖这是覆盖辅助模块中的字符串,会再次转变为正确的。
This sounds very confusing, but remember ( https://docs.python.org/3/reference/import.html#submodules ):这听起来很混乱,但请记住( https://docs.python.org/3/reference/import.html#submodules ):
When a submodule is loaded using any mechanism (eg importlib APIs, the import or import-from statements, or built-in
__import__()
) a binding is placed in the parent module's namespace to the submodule object.当使用任何机制(例如 importlib API、import 或 import-from 语句或内置
__import__()
)__import__()
子模块时,将在父模块的命名空间中放置一个绑定到子模块对象。 For example, if package spam has a submodule foo, after importing spam.foo, spam will have an attribute foo which is bound to the submodule.例如,如果包 spam 有一个子模块 foo,那么在导入 spam.foo 后,spam 将有一个属性 foo 绑定到子模块。 Let's say you have the following directory structure:
假设您有以下目录结构:
An import parent.a
first runs all the module set-up code, and then binds the name.一个
import parent.a
首先运行所有模块设置代码,然后绑定名称。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.