英文:
Module name binding by relative import in __init__.py
问题
我编写了一个包,其中包含两个子模块:
pkg/init.py
pkg/foo.py
pkg/bar.py
我将以下代码放在__init__.py
和bar.py
中。
from . import foo as f
foo
print("Hello!")
虽然导入pkg
成功,但导入pkg.bar
不成功:
>>> import pkg
Hello!
>>> import pkg.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\fumito\Documents\pkg\bar.py", line 2, in <module>
foo
NameError: name 'foo' is not defined
为什么foo
在pkg
命名空间中定义了,但在pkg.bar
中没有定义?
英文:
I wrote a package containing two submodules:
pkg/__init__.py
pkg/foo.py
pkg/bar.py
I put the following code in __init__.py
and also in bar.py
.
from . import foo as f
foo
print("Hello!")
While importing pkg
suceeds, pkg.bar
doesn't:
>>> import pkg
Hello!
>>> import pkg.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\fumito\Documents\pkg\bar.py", line 2, in <module>
foo
NameError: name 'foo' is not defined
Why is foo
defined in the pkg
namespace but not in the pkg.bar
?
答案1
得分: 0
通过导入foo
模块,它隐式地成为pkg
的属性,因为它的完全限定位置是pkg.foo
。这使得名称foo
在__init__.py
中可用,因为它变成了pkg
/__init__.py
的属性。
但显然,pkg.bar.foo
是没有意义的,因为那不是foo
模块的完全限定名称,所以在bar
中导入foo
不会在bar
内创建一个名为foo
的名称。
(这是一个相当模糊的解释,但总结了这里发挥作用的核心机制。)
英文:
By importing the foo
module, it implicitly becomes an attribute of pkg
, because its fully qualified location is pkg.foo
. This makes the name foo
available in __init__.py
, because it became an attribute of pkg
/__init__.py
.
But obviously, pkg.bar.foo
would make no sense, since that's not the fully qualified name of the foo
module, so importing foo
in bar
does not create a name foo
inside bar
.
(This is a rather handwavey explanation, but summarises the core mechanism at play here.)
答案2
得分: -3
以下是您要翻译的内容:
EDIT : This answer is incorrect. Please read the explanation in the next section.
这是因为当您尝试在与 pkg
相同的目录中导入模块时,相对导入发生在 pkg
所在的位置。这意味着 bar.py
中的以下语句
from . import foo as f
试图在 pkg
的父目录中查找 foo
。
您可以选择在 pkg/bar.py
中写入以下导入语句(我个人更喜欢这种方法,因为它不修改路径)
import pkg.foo as f
或者您可以选择在 __init__.py
文件中修改路径变量
import os, sys
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(script_dir)
然后,在 bar.py
中添加以下导入语句
import foo as f
===============================================================
ATTEMPT 2
因此,deceze 提供了一个非常好的答案,我将尝试进行扩展。
您所做的事情与 Python 文档中的 5.4.2. 子模块 中所述非常相似:
> 当使用任何机制(例如 importlib
API、import
或 import-from
语句或内置的 __import__()
)加载子模块时,将在父模块的命名空间中放置一个与子模块对象绑定的绑定。例如,如果包 spam
有一个子模块 foo
,在导入 spam.foo
后,spam
将有一个属性 foo
,该属性绑定到子模块。假设您有以下目录结构:
spam/
__init__.py
foo.py
> 并且 spam/__init__.py
中有以下行:
from .foo import Foo
> 那么执行以下操作会将 foo
和 Foo
的名称绑定放入 spam
模块中:
>>>import spam
>>>spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>>spam.Foo
<class 'spam.foo.Foo'>
>根据 Python 的熟悉名称绑定规则,这可能看起来令人惊讶,但实际上这是导入系统的一个基本特性。保持不变的是,如果您有 sys.modules['spam']
和 sys.modules['spam.foo']
(如上述导入后所示),则后者必须出现为前者的 foo
属性。
与文档中提到的类似,当您在 __init__.py
中添加语句
# pkg/__init__.py
from . import foo as f
foo
print("Hello!")
然后导入 pkg
时,由于导入语句的存在,pkg.foo
存在,这就是为什么不会引发 NameError
。因此,导入 pkg
将成功。您可以检查终端中的 pkg.foo
的值,您将看到与以下类似的返回值:
<module 'pkg.foo' from 'absolute/path/to/pkg/foo.py'>
现在,在 bar.py
中添加以下代码
# pkg/bar.py
from . import foo as f
foo
print("Hello!")
并尝试执行
import pkg.bar
会引发 NameError
异常,因为在 bar.py
中不存在 foo
对象;foo
不存在于 bar.py
的命名空间中。
当您在 __init__.py
中使用别名导入 foo
时,还发生了一些有趣的事情。实际上,您可以在仍然可以访问 foo.py
内容的情况下重写引用 pkg.foo
。如果您在 __init__.py
文件之后添加以下块
class foo:
pass
然后检查引用 pkg.foo
的值将返回
<class 'pkg.foo'>
但仍然可以通过使用 pkg.f
访问 foo.py
的内容。如果您检查 pkg.f
的值,将看到类似以下内容的内容:
<module 'pkg.foo' from 'absolute/path/to/pkg/foo.py'>
英文:
EDIT : This answer is incorrect. Please read the explanation in the next section.
It is because when you try to import the modules when you are in the same directory as pkg
, the relative import happens from where pkg
is. That means that the following statement in bar.py
from . import foo as f
attempts to find foo
in the parent directory of pkg
.
You could choose to write the following import statement in pkg/bar.py
instead (I personally prefer this method because it doesn't modify the path)
import pkg.foo as f
Or you could choose to modify the path variable within your __init__.py
file
import os, sys
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(script_dir)
And then, add the following import statement in bar.py
import foo as f
===============================================================
ATTEMPT 2
So deceze has provided a really nice answer which I will try to expand upon.
What you are doing is quite similar to what is stated in the Python docs in 5.4.2. Submodules:
> When a submodule is loaded using any mechanism (e.g. 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. 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. Let’s say you have the following directory structure:
spam/
__init__.py
foo.py
> and spam/__init__.py
has the following line in it:
from .foo import Foo
>then executing the following puts name bindings for foo
and Foo
in the spam
module:
>>>import spam
>>>spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>>spam.Foo
<class 'spam.foo.Foo'>
>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']
and sys.modules['spam.foo']
(as you would after the above import), the latter must appear as the foo
attribute of the former.
Similar to what is mentioned in the docs, when you add the statement
# pkg/__init__.py
from . import foo as f
foo
print("Hello!")
in __init__.py
and then import pkg
, pkg.foo
exists because of the import statement, which is why NameError
is not raised. Therefore, importing pkg
will be successful. You can check that pkg.foo
is referring to foo.py
if you check the value of pkg.foo
within the terminal - you will see a similar return value to
<module 'pkg.foo' from 'absolute/path/to/pkg/foo.py'>
Now adding the following code in bar.py
# pkg/bar.py
from . import foo as f
foo
print("Hello!")
and attempting to execute
import pkg.bar
does raise the NameError
exception because an object foo
does not exist within bar.py
; foo
does not exist within the namespace of bar.py
.
There is something interesting that is also happening when you used an alias to import foo
within __init__.py
. It is actually possible to overwrite the reference pkg.foo
while still maintaining access to the contents of foo.py
. If you added the following block within your __init__.py
file after foo
class foo:
pass
then checking the reference pkg.foo
will return
<class 'pkg.foo'>
but contents of foo.py
can still be accessed by using pkg.f
. If you check the value of pkg.f
, you will see someting similar to
<module 'pkg.foo' from 'absolute/path/to/pkg/foo.py'>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论