将代码添加到__init__.py


85

我正在研究django中的模型系统如何工作,我注意到一些我不理解的东西。

我知道您创建了一个空__init__.py文件来指定当前目录是一个包。并且您可以在其中设置一些变量,__init__.py以便import *正常工作。

但是django添加了一堆from ... import ...语句,并在中定义了一堆类__init__.py。为什么?这不只是使事情看起来凌乱吗?是否有需要此代码的原因__init__.py


13
这不是真的与Django有关吗?是的,您首先是在Django中看到的,但这似乎更像是纯Python的东西-也许Django标签确实不合适。
S.Lott

我在__init__.pydjango 1.8中看不到任何import语句。这是旧版本吗?如果是哪个版本?
Gobi Dasu

Answers:


72

__init__.py当导入包含它的包(目录)时,所有导入都可用。

例:

./dir/__init__.py

import something

./test.py

import dir
# can now use dir.something

编辑:忘了提及,__init__.py您第一次从该目录导入任何模块时,中的代码就会运行。因此,通常是放置任何程序包级初始化代码的好地方。

EDIT2:dgrant指出了我的示例中可能存在的混乱。In__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导入包或包中的模块时,在包命名空间中将自动提供在中分配的所有名称(无论是导入的模块,函数还是类)。


好的谢谢。但是我仍然不确定为什么将类添加到一个好主意,而__init__.py 我实际上并不考虑这些类的初始化代码(但是也许我错了)。
Erik

这些可能是您每次使用软件包时都有用的类。但我不想推测,可能有很多原因使它们在那里,无论客观与否:)
亚历山大·科耶夫尼科夫

13
这也可能是出于历史原因。将模块转换为包时,将module.py转换为module / __ init__.py时,所有现有代码都可以像以前一样使用它,但是现在module可以具有子模块。
卢卡斯

1
模块__init__.py隐式执行父级。通过在内部导入模块__init__.py,可以创建循环导入。在__init__.py完全这样一个导入之前将不会被执行。__init__.py清空是比较安全的。
伊沃·丹尼赫尔卡

重要的是要指出这不是__init__.py文件专用的。如果您有一个dir/other.py类似文件的文件,from datetime import datetime您也可以调用dir.other.datetime.now()甚至from dir.other import datetime
卡莱斯·萨拉

37

确实,这只是个人喜好,并且与python模块的布局有关。

假设您有一个名为的模块erikutils。它可以通过两种方式成为模块,或者有一个名为erikutils.py的文件。sys.path或您有一个目录名为erikutilssys.path用空__init__.py里面的文件。然后让我们说你有一堆模块调用的fileutilsprocutilsparseutils和你想那些要承受子模块erikutils。因此,您制作了一些名为fileutils.pyprocutils.pyparseutils.py的.py文件:

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

也许你有几个功能,只是不属于在fileutilsprocutilsparseutils模块。假设您不想创建一个名为的新模块miscutils。AND,您希望能够像这样调用该函数:

erikutils.foo()
erikutils.bar()

而不是做

erikutils.miscutils.foo()
erikutils.miscutils.bar()

因此,由于erikutils模块是目录,而不是文件,因此我们必须在__init__.py文件内部定义其功能。

在django中,我能想到的最好的例子是django.db.models.fields。所有django * Field类都在django / db / models / fields目录中的__init__.py文件中定义。我想他们这样做是因为他们不想把所有东西都塞进假设的Django / DB /模型/ fields.py模式,所以他们分裂出来成几个子模块(related.pyfiles.py,例如)和他们将制造的* Field定义粘贴在fields模块本身中(因此,__init__.py)。


1
dgrant,我的意思是something可以是一个外部模块,dir.some会起作用。感谢您的评论,我将编辑我的帖子以使其更加清晰。
亚历山大·科耶夫尼科夫

29

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

从外部看,包装看上去仍然和以前一样。


1
@Arlen:重点是它不是公共API的一部分。如果重命名模块,则可以确保没有相关代码中断。另外,这确保了API元素仅出现一次,例如,当使用自省自动创建API文档时。
nikow 2011年

5
@Arlen:删除模块可防止一个模块import <pack>.somemodule1直接被删除。您只能从<pack>在其或未__init__.py删除子模块中定义或导入的对象中导入。
MestreLion'4
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.