【问题标题】:Adding code to __init__.py将代码添加到 __init__.py
【发布时间】:2010-09-12 06:16:00
【问题描述】:

我正在查看 django 中的模型系统是如何工作的,我发现了一些我不明白的地方。

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

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

【问题讨论】:

  • 这真的不是关于 Django 的吗?是的,你第一次在 Django 中看到它,但这看起来更像是一个纯 Python 的东西——也许 Django 标记并不合适。
  • 我在__init__.pydjango 1.8 中看不到任何导入语句。这是旧版本的吗?如果有,是哪个版本?

标签: python initialization package


【解决方案1】:

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

例子:

./dir/__init__.py:

import something

./test.py:

import dir
# can now use dir.something

编辑:忘了提一下,__init__.py 中的代码在您第一次从该目录导入任何模块时运行。所以它通常是放置任何包级初始化代码的好地方。

EDIT2:dgrant 在我的示例中指出了可能的混淆。在__init__.py import something 可以导入任何模块,不需要从包中导入。例如,我们可以将其替换为import datetime,然后在我们的顶级test.py 中,这两个sn-ps 都可以工作:

import dir
print dir.datetime.datetime.now()

import dir.some_module_in_dir
print dir.datetime.datetime.now()

底线是:在__init__.py 中分配的所有名称,无论是导入的模块、函数还是类,在您导入包或包中的模块时自动在包命名空间中可用。

【讨论】:

  • 好的,谢谢。但是我仍然不确定为什么将类添加到__init__.py 是一个好主意我并没有真正考虑这些类的初始化代码(但也许我错了)。
  • 这些可能是您每次使用包时有用的类。但我不想推测,它们存在的原因可能有很多,客观与否:)
  • 这也可能是历史原因。当您将模块转换为包时,module.py 到 module/__init__.py 所有现有代码都可以像以前一样使用它,但现在模块可以有子模块。
  • 模块隐式执行父__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
【解决方案2】:

这只是个人喜好,与你的 python 模块的布局有关。

假设您有一个名为erikutils 的模块。有两种方法可以将其作为模块,或者您在 sys.path 上有一个名为 erikutils.py 的文件,或者您在 @987654323 上有一个名为 erikutils 的目录@ 里面有一个空的 __init__.py 文件。然后假设您有一堆模块,称为fileutilsprocutilsparseutils,并且您希望这些模块是erikutils 下的子模块。因此,您制作了一些名为 fileutils.pyprocutils.pyparseutils.py 的 .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 类都在 django/db/models/fields 目录下的 __init__.py 文件中定义。我猜他们这样做是因为他们不想把所有东西都塞进一个假设的 django/db/models/fields.py 模型中,所以他们把它分成了几个子模块(相关的。 py,例如 files.py),他们将制作的 *Field 定义粘贴在 fields 模块本身中(因此,__init__.py)。 p>

【讨论】:

  • dgrant,我的意思是something 可以是一个外部模块,dir.something 仍然可以工作。感谢您的评论,我将编辑我的帖子以使其更加清晰。
【解决方案3】:

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

从外面看,包裹还是和以前一样。

【讨论】:

  • @Arlen:关键是它不是公共 API 的一部分。如果您重命名一个模块,您可以确保没有相关代码中断。此外,这可确保 API 元素仅出现一次,例如,当使用自省自动创建 API 文档时。
  • @Arlen:删除一个模块会阻止一个模块直接来自import <pack>.somemodule1。您只能从在其__init__.py 中定义或导入的<pack> 对象以及未删除的子模块中导入。
猜你喜欢
  • 1970-01-01
  • 2014-08-04
  • 1970-01-01
  • 2011-08-15
  • 1970-01-01
  • 2012-03-22
  • 2013-05-24
  • 2011-01-22
  • 1970-01-01
相关资源
最近更新 更多