在导入的 __init__.py 中,导入的行为不同 [英] Imports behave differently when in __init__.py that is imported
问题描述
__init__.py
中的导入在文件运行和导入时的行为似乎不同.
如果我们有以下文件:
run.py
:
导入测试
test/b.py
:
B 类(对象):经过
test/__init__.py
:
from b import B打印 B打印 b
如果我们运行 __init__.py
,我们会得到一个预期的错误:
% python test/__init__.py<类'b.B'>回溯(最近一次调用最后一次):文件test/__init__.py",第 6 行,在 <module> 中打印 bNameError: 名称 'b' 未定义
但是如果我们 run.py
那么我们不会:
% python run.py<类'test.b.B'><来自'~/temp/test/b.py'的模块'test.b'>
我希望行为是相同的.为什么这样做?
这仅在我们在 __init__.py
中执行时才有效.如果我们:
mv __init__.py a.py触摸 __init__.py
并使run.py
:
导入 test.a
然后我们确实得到了错误.
情况如下:你有一个脚本(run.py
),一个包test
及其子模块 test.b
.
每当您在 Python 中导入子模块时,该子模块的名称都会自动存储到父包中.因此,当您执行 import collections.abc
(或 from collections.abc import Iterable
或类似操作)时,包 collections
会自动获取该属性abc
.
这就是这里发生的事情.当你这样做时:
from b import B
名称b
会自动加载到test
中,因为b
是test
包的子模块.
即使您没有明确地执行import b
,每当您导入该模块时,名称都会放入test
.因为b
是test
的子模块,属于test
.
侧节点:您的代码不适用于 Python 3,因为 相关导入已被移除.要使您的代码与 Python 3 一起使用,您必须编写:
from test.b import B
此语法与 from b import B
完全相同,但更加明确,应该有助于您理解发生了什么.
参考:Python 参考文档 还解释了这种行为,并包含了一个与这种情况非常相似的有用示例(区别只是使用了绝对导入,而不是相对导入).
<块引用>当使用任何机制(例如 importlib
API、import
或 import-from
语句或内置 __import__()
) 一个绑定被放置在父模块的命名空间中到子模块对象.比如包spam
有一个子模块foo
,导入spam.foo
后,spam
就会有一个属性foo
绑定到子模块.
假设您有以下目录结构:
垃圾邮件/__init__.py文件酒吧.py
和 spam/__init__.py
中有以下几行:
from .foo import Foo从 .bar 导入栏
然后执行以下操作将名称绑定到 foo
和 bar
到 spam
模块中:
鉴于 Python 熟悉的名称绑定规则,这可能看起来令人惊讶,但它实际上是导入系统的基本功能.不变的是,如果你有 sys.modules['spam']
和 sys.modules['spam.foo']
(就像你在上面导入后那样),后者必须作为前者的 foo
属性出现.
Imports in an __init__.py
seem to behave differently when the file is run, to when it is imported.
If we have the following files:
run.py
:
import test
test/b.py
:
class B(object):
pass
test/__init__.py
:
from b import B
print B
print b
If we run __init__.py
we get an error as I expect:
% python test/__init__.py
<class 'b.B'>
Traceback (most recent call last):
File "test/__init__.py", line 6, in <module>
print b
NameError: name 'b' is not defined
But if we run.py
then we don't:
% python run.py
<class 'test.b.B'>
<module 'test.b' from '~/temp/test/b.py'>
I would expect the behaviour to be the same. Why does this work?
This only works if we do it in an __init__.py
. If we:
mv __init__.py a.py
touch __init__.py
and make run.py
:
import test.a
Then we do get the error.
The situation is the following: you have a script (run.py
), a package test
and its submodule test.b
.
Whenever you import a submodule in Python, the name of that submodule is automatically stored into the parent package. So that when you do import collections.abc
(or from collections.abc import Iterable
, or similar), the package collections
automatically gets the attribute abc
.
This is what's happening here. When you do:
from b import B
the name b
is automatically loaded into test
, because b
is a submodule of the test
package.
Even if you don't do import b
explicitly, whenever you import that module, the name is placed into test
. Because b
is a submodule of test
, and it belongs to test
.
Side node: your code won't work with Python 3, because relative imports have been removed. To make your code work with Python 3, you would have to write:
from test.b import B
This syntax is perfectly identical to from b import B
, but is much more explicit, and should help you understand what's going on.
Reference: the Python reference documentation also explains this behavior, and includes a helpful example, very similar to this situation (the difference is just that an absolute import is used, instead of a relative import).
When a submodule is loaded using any mechanism (e.g.
importlib
APIs, theimport
orimport-from
statements, or built-in__import__()
) a binding is placed in the parent module's namespace to the submodule object. For example, if packagespam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule.Let's say you have the following directory structure:
spam/ __init__.py foo.py bar.py
and
spam/__init__.py
has the following lines in it:from .foo import Foo from .bar import Bar
then executing the following puts a name binding to
foo
andbar
in thespam
module:>>> import spam >>> spam.foo <module 'spam.foo' from '/tmp/imports/spam/foo.py'> >>> spam.bar <module 'spam.bar' from '/tmp/imports/spam/bar.py'>
Given Python's familiar name binding rules this might seem surprising, but it's actually a fundamental feature of the import system. The invariant holding is that if you have
sys.modules['spam']
andsys.modules['spam.foo']
(as you would after the above import), the latter must appear as thefoo
attribute of the former.
这篇关于在导入的 __init__.py 中,导入的行为不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!