在__init__.py中添加代码
我在研究Django的模型系统时,发现了一些我不太理解的地方。
我知道要创建一个空的 __init__.py
文件,以表明当前目录是一个包。而且你可以在 __init__.py
中设置一些变量,这样使用 import * 时才能正常工作。
但是Django在 __init__.py
中添加了很多 from ... import ... 的语句,并定义了很多类。这是为什么呢?难道这样不会让事情看起来很乱吗?有没有什么特别的原因需要在 __init__.py
中写这些代码呢?
4 个回答
使用 __init__.py
文件可以让你把内部的包结构对外隐藏起来。如果内部结构发生变化(比如把一个大模块拆分成两个小模块),你只需要调整 __init__.py
文件,而不需要去改动依赖这个包的其他代码。你还可以让包的某些部分对外不可见,比如那些还没有准备好供大家使用的部分。
需要注意的是,你可以使用 del
命令,所以一个典型的 __init__.py
文件可能长这样:
from somemodule import some_function1, some_function2, SomeObject
del somemodule
现在如果你决定拆分 somemodule
,新的 __init__.py
可能会是:
from somemodule1 import some_function1, some_function2
from somemodule2 import SomeObject
del somemodule1
del somemodule2
从外部来看,这个包依然和之前完全一样。
这其实只是个人喜好,跟你怎么安排你的 Python 模块有关系。
假设你有一个模块叫做 erikutils
。这个模块可以有两种形式:要么你有一个叫 erikutils.py 的文件在你的 sys.path
里,要么你有一个叫 erikutils 的文件夹在你的 sys.path
里,里面有一个空的 __init__.py
文件。接着,假设你还有一些模块叫 fileutils
、procutils
和 parseutils
,你想把它们放在 erikutils
下面,作为子模块。于是你创建了一些 .py 文件,分别叫 fileutils.py、procutils.py 和 parseutils.py:
erikutils
__init__.py
fileutils.py
procutils.py
parseutils.py
可能你有一些函数,不太适合放在 fileutils
、procutils
或 parseutils
模块里。假设你又不想创建一个新的模块叫 miscutils
。而且,你希望能像这样调用这个函数:
erikutils.foo()
erikutils.bar()
而不是这样:
erikutils.miscutils.foo()
erikutils.miscutils.bar()
所以因为 erikutils
模块是一个文件夹,而不是一个文件,我们就得把它的函数定义在 __init__.py
文件里。
在 Django 中,我能想到的一个很好的例子就是 django.db.models.fields
。所有的 Django *Field 类都是在 __init__.py
文件中定义的,位置在 django/db/models/fields 这个文件夹里。我想他们这样做是因为不想把所有东西都塞进一个假想的 django/db/models/fields.py 文件里,所以他们把它拆分成几个子模块(比如 related.py、files.py),然后把 *Field 的定义放在了 fields 模块本身里(也就是 __init__.py
)。
在 __init__.py
文件中的所有导入都会在你导入包含它的包(文件夹)时可用。
举个例子:
./dir/__init__.py
:
import something
./test.py
:
import dir
# can now use dir.something
补充一下:我忘了提,__init__.py
中的代码会在你第一次从那个文件夹导入任何模块时运行。所以通常把一些包级别的初始化代码放在这里是个好主意。
再补充一下:dgrant 指出我例子中可能会让人困惑的地方。在 __init__.py
中,import something
可以导入任何模块,不一定是来自这个包的。例如,我们可以把它换成 import datetime
,那么在我们的顶层 test.py
中,这两个代码片段都会有效:
import dir
print dir.datetime.datetime.now()
还有
import dir.some_module_in_dir
print dir.datetime.datetime.now()
总之:在 __init__.py
中赋值的所有名称,无论是导入的模块、函数还是类,都会在你导入这个包或包中的模块时自动可用。