python:.什么是__init__.py?

在Python源目录中__init__.py是干什么的?


它是一个整体的一部分。这是文档。

The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later (deeper) on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.


名为__init__.py的文件用于将磁盘上的目录标记为Python包目录。如果你有文件的话

1
2
mydir/spam/__init__.py
mydir/spam/module.py

并且mydir在您的路径上,您可以导入module.py as中的代码

1
import spam.module

1
from spam import module

如果删除__init__.py文件,Python将不再在该目录中查找子模块,因此导入该模块的尝试将失败。

__init__.py文件通常是空的,但是可以用来导出包中选定的部分,使用更方便的名称,保存方便的函数,等等。在上面的例子中,init模块的内容可以被访问为

1
import spam

在此基础上


除了将目录标记为Python包并定义__all__之外,__init__.py还允许您在包级别定义任何变量。如果包定义了以类api方式频繁导入的内容,那么这样做通常很方便。这种模式促进了对python"平面优于嵌套"哲学的坚持。

一个例子

下面是我的一个项目中的一个例子,其中我经常导入一个名为Sessionsessionmaker来与我的数据库交互。我写了一个"数据库"包,有几个模块:

1
2
3
4
5
database/
    __init__.py
    schema.py
    insertions.py
    queries.py

My __init__.py包含以下代码:

1
2
3
4
5
6
7
import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

engine = create_engine(os.environ['DATABASE_URL'])
Session = sessionmaker(bind=engine)

因为我在这里定义了Session,所以我可以使用下面的语法启动一个新的会话。这段代码将在"database"包目录的内部或外部执行。

1
2
from database import Session
session = Session()

当然,这是一个小小的便利——另一种方法是在一个像"create_session"这样的新文件中定义Session。,并开始新的会话使用:

1
2
from database.create_session import Session
session = Session()

进一步阅读

reddit上有一个非常有趣的帖子,介绍了__init__.py的正确用法:

What's your opinion on what to include in __init__.py ? from Python

大多数意见似乎是__init__.py文件应该非常薄,以避免违反"显式优于隐式"的哲学。


__init__.py有两个主要原因

为了方便:其他用户不需要知道函数在包层次结构中的确切位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
your_package/
  __init__.py
  file1.py/
  file2.py/
    ...
  fileN.py

# in __init__.py
from file1 import *
from file2 import *
...
from fileN import *

# in file1.py
def add():
    pass

然后其他人可以调用add() by

1
from your_package import add

不知道file1

1
from your_package.file1 import add

如果你想要初始化一些东西;例如,日志(应该放在顶层):

1
2
import logging.config
logging.config.dictConfig(Your_logging_config)


__init__.py文件使Python将包含它的目录视为模块。

此外,这是模块中要加载的第一个文件,因此您可以使用它来执行每次加载模块时要运行的代码,或者指定要导出的子模块。


自Python 3.3以来,__init__.py不再需要将目录定义为可导入的Python包。

检查PEP 420:隐式名称空间包:

Native support for package directories that don’t require __init__.py marker files and can automatically span multiple path segments (inspired by various third party approaches to namespace packages, as described in PEP 420)

这是测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ mkdir -p /tmp/test_init
$ touch /tmp/test_init/module.py /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
├── module.py
└── __init__.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

$ rm -f /tmp/test_init/__init__.py
$ tree -at /tmp/test_init
/tmp/test_init
└── module.py
$ python3

>>> import sys
>>> sys.path.insert(0, '/tmp')
>>> from test_init import module
>>> import test_init.module

引用:https://docs.python.org/3/whatsnew/3.3.html#pep-420-implicit-namespace-packageshttps://www.python.org/dev/peps/pep-0420/Python 3中的包是否不需要.py ?


在Python中,包的定义非常简单。与Java一样,层次结构和目录结构是相同的。但是您必须在包中包含__init__.py。我将用下面的例子来解释__init__.py文件:

1
2
3
4
5
6
7
8
9
10
package_x/
|--  __init__.py
|--    subPackage_a/
|------  __init__.py
|------  module_m1.py
|--    subPackage_b/
|------  __init__.py
|------  module_n1.py
|------  module_n2.py
|------  module_n3.py

__init__.py可以是空的,只要它存在。它表明目录应该被看作一个包。当然,__init__.py也可以设置适当的内容。

如果我们在module_n1中添加一个函数:

1
2
3
def function_X():
    print"function_X in module_n1"
    return

在运行:

1
2
3
4
>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()

function_X in module_n1

然后,我们按照层次结构包调用module_n1函数。我们可以像这样在subPackage_b中使用__init__.py:

1
__all__ = ['module_n2', 'module_n3']

在运行:

1
2
3
4
5
6
>>>from package_x.subPackage_b import *
>>>module_n1.function_X()

Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
ImportError: No module named module_n1

因此,使用* import,模块包受制于__init__.py内容。


__init__.py将把它所在的目录视为一个可加载模块。

对于喜欢阅读代码的人,我在这里添加了两位炼金术士的注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ find /tmp/mydir/
/tmp/mydir/
/tmp/mydir//spam
/tmp/mydir//spam/__init__.py
/tmp/mydir//spam/module.py
$ cd ~
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
>>> module.myfun(3)
9
>>> exit()
$
$ rm /tmp/mydir/spam/__init__.py*
$
$ python
>>> import sys
>>> sys.path.insert(0, '/tmp/mydir')
>>> from spam import module
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
ImportError: No module named spam
>>>

虽然Python没有__init__.py文件,但您仍然应该包含一个。

它指定一个包应该被视为一个模块,因此包括它(即使它是空的)。

也有一种情况,你可以实际使用__init__.py文件:

假设您有以下文件结构:

1
2
main_methods
    |- methods.py

其中包括:

1
2
def foo():
    return 'foo'

要使用foo(),您需要以下之一:

1
2
3
from main_methods.methods import foo # Call with foo()
from main_methods import methods # Call with methods.foo()
import main_methods.methods # Call with main_methods.methods.foo()

也许您需要(或希望)将methods.py保存在main_methods中(例如,运行时/依赖项),但是您只想导入main_methods

如果您将methods.py的名称更改为__init__.py,那么只需导入main_methods就可以使用foo():

1
2
import main_methods
print(main_methods.foo()) # Prints 'foo'

这是因为__init__.py被视为包的一部分。

一些Python包实际上是这样做的。JSON就是一个例子,其中运行import json实际上是从json包中导入__init__.py(参见这里的包文件结构):

Source code: Lib/json/__init__.py


它便于导入其他python文件。当您将此文件放在包含其他py文件的目录中(比如stuff)时,您可以执行导入stuff.other之类的操作。

1
2
3
4
5
6
root\
    stuff\
         other.py

    morestuff\
         another.py

如果目录中没有这个__init__.py,就不能导入其他内容。因为Python不知道这些东西的源代码在哪里,也无法将其识别为一个包。