使用__init__.py的Python子模块导入
我正在学习Python,但我搞不清楚__init__.py
里的导入是怎么回事。
我从Python教程了解到,__init__.py
文件是用来初始化一个包的,我可以在这里导入子包。
不过,我好像做错了什么。你能帮我解释一下(也帮助将来的Python学习者)我哪里出错了吗?
这是我尝试做的一个简化示例。
这是我的文件结构:
package
__init__.py
test.py
subpackage
__init__.py
hello_world.py
hello_world.py
的内容是:
def do_something():
print "Hello, world!"
subpackage/__init__.py
是空的。
package/__init__.py
包含:
import test.submodule.do_something
最后,test.py
的内容是:
do_something()
这是我尝试通过OSX终端和Python 3运行hello_world.py的方式:
python test.py
然后,Python抛出了以下错误:
NameError: name 'do_something' is not defined
3 个回答
在Python中,有一个绝对不变的规则,就是你使用的名字必须在你所在的模块中定义或者导入。在这里,你在test.py文件里没有导入任何东西,所以就像错误信息所说的那样,do_something
没有被定义。
即使你的package/__init__.py
文件被执行了(不过,正如其他人所指出的,它并没有被执行),你的代码还是无法正常工作,因为如果你想在test.py文件中使用do_something
,你必须在test.py里导入它。
首先,你得明白单独使用 import
是怎么回事:
import test.submodule.do_something
这会尝试从已经加载的 test
中的 submodule
加载 do_something
。
你想从 subpackage
中加载东西,所以先从这里开始:
import subpackage
好的,subpackage/__init__.py
被加载了。
现在,你想要的 do_something()
函数在一个文件(也叫“模块”)hello_world.py
中。很简单:
from subpackage.hello_world import do_something
这样就完成了!大声读出这一行,它做的正是它所说的:从 subpackage
包中的 hello_world
模块导入 do_something
。
在 test.py
中试试这个:
from subpackage.hello_world import do_something
do_something()
应该可以正常工作。
接下来,第二个问题:
__init__.py
在 package/
中不会被调用,因为你没有把 package/
当作一个包来使用。只有当你导入 package/
或者其中的任何东西时,__init__.py
才会被使用,比如:
from package import test
否则,它根本不会被加载。
不过,如果你想在导入子包时加载 do_something()
,就把 from submodule.hello_world import do_something
放在 subpackage/__init__.py
中,然后在你的 test.py
中执行 import subpackage
。
你可能已经明白,当你导入一个模块时,解释器会创建一个新的命名空间,并在这个新的命名空间中执行模块的代码,这个命名空间既是局部的也是全局的。当代码执行完毕后,模块名(或者在任何as
子句中给出的名称)会与刚刚创建的模块对象绑定,并记录在sys.modules
中的__name__
里。
当你导入一个像package.subpackage.module
这样的合格名称时,首先会将第一个名称(package
)导入到局部命名空间,然后将subpackage
导入到package
的命名空间,最后将module
导入到package.subpackage
的命名空间。使用from ... import ... as ...
的导入方式也会执行相同的操作,但导入的对象会直接绑定到导入模块的命名空间中。包名没有绑定在你的局部命名空间中,并不意味着它没有被导入(查看sys.modules
就能看到)。
包中的__init__.py
文件的作用和模块的.py
文件差不多。一个包是有结构的,它以目录的形式存在,这个目录可以包含模块(普通的.py
文件)和子目录(也包含__init__.py
文件)用于任何子包。当包被导入时,会创建一个新的命名空间,并在这个命名空间中执行包的__init__.py
,这个命名空间既是局部的也是全局的。所以为了回答你的问题,我们可以通过省略顶层包来简化你的文件存储,这样在运行test.py
时,解释器就不会考虑它。它看起来会是这样的:
test.py
subpackage/
__init__.py
hello_world.py
现在,subpackage
不再是一个子包,因为我们已经去掉了不相关的包含包。关注一下为什么do_something
这个名字是未定义的可能会有所帮助。test.py
没有包含任何导入,所以不清楚你是怎么期待do_something
有意义的。你可以通过使用一个空的subpackage/__init__.py
来让它工作,然后你可以将test.py
写成:
from subpackage.hello_world import do_something
do_something()
或者你可以使用一个读取如下内容的subpackage/__init__.py
:
from hello_world import do_something
这会在导入包时在subpackage
命名空间中建立do_something
函数。然后使用一个导入这个函数的test.py
,像这样:
from subpackage import do_something
do_something()
最后一种选择是使用相同的__init__.py
,并编写一个test.py
,它简单地导入(子)包,然后使用相对命名来访问所需的函数:
import subpackage
subpackage.do_something()
这样就可以在你的局部命名空间中访问它。
使用空的__init__.py
,这也可以通过一个读取如下内容的test.py
来实现:
import subpackage.hello_world
subpackage.hello_world.do_something()
甚至可以是:
from subpackage.hello_world import do_something
do_something()
一个空的__init__.py
意味着顶层包的命名空间只会包含程序导入的任何子包的名称,这样你就可以只导入你需要的子包。这让你可以控制顶层包的命名空间。
虽然在__init__.py
中定义类和函数是完全可以的,但更常见的做法是从子模块中导入内容到这个命名空间,这样导入者只需导入顶层包,就能通过单层属性引用访问其内容,或者使用from
只导入你特别想要的名称。
最终,保持清晰的理解导入是如何工作的,以及它的各种形式对导入命名空间的影响,是最好的工具。