在__init__.py中添加代码

104 投票
4 回答
82906 浏览
提问于 2025-04-11 09:21

我在研究Django的模型系统时,发现了一些我不太理解的地方。

我知道要创建一个空的 __init__.py 文件,以表明当前目录是一个包。而且你可以在 __init__.py 中设置一些变量,这样使用 import * 时才能正常工作。

但是Django在 __init__.py 中添加了很多 from ... import ... 的语句,并定义了很多类。这是为什么呢?难道这样不会让事情看起来很乱吗?有没有什么特别的原因需要在 __init__.py 中写这些代码呢?

4 个回答

30

使用 __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

从外部来看,这个包依然和之前完全一样。

44

这其实只是个人喜好,跟你怎么安排你的 Python 模块有关系。

假设你有一个模块叫做 erikutils。这个模块可以有两种形式:要么你有一个叫 erikutils.py 的文件在你的 sys.path 里,要么你有一个叫 erikutils 的文件夹在你的 sys.path 里,里面有一个空的 __init__.py 文件。接着,假设你还有一些模块叫 fileutilsprocutilsparseutils,你想把它们放在 erikutils 下面,作为子模块。于是你创建了一些 .py 文件,分别叫 fileutils.pyprocutils.pyparseutils.py

erikutils
  __init__.py
  fileutils.py
  procutils.py
  parseutils.py

可能你有一些函数,不太适合放在 fileutilsprocutilsparseutils 模块里。假设你又不想创建一个新的模块叫 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.pyfiles.py),然后把 *Field 的定义放在了 fields 模块本身里(也就是 __init__.py)。

82

__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 中赋值的所有名称,无论是导入的模块、函数还是类,都会在你导入这个包或包中的模块时自动可用。

撰写回答