__init__.py的作用是什么?


Answers:


1452

它曾经是软件包的必需部分(旧的3.3之前的“常规软件包”,而不是新的3.3+“命名空间软件包”)。

这是文档。

Python定义了两种类型的程序包,常规程序包和名称空间程序包。常规软件包是Python 3.2及更早版本中存在的传统软件包。常规软件包通常实现为包含__init__.py文件的目录。导入常规程序包时,__init__.py将隐式执行此文件,并将其定义的对象绑定到程序包名称空间中的名称。该__init__.py文件可以包含任何其他模块可以包含的相同Python代码,并且Python在导入时会向该模块添加一些其他属性。

但是只需单击链接,它就会包含一个示例,更多信息以及对名称空间包的说明,这些名称空间包不含__init__.py


187
这是什么意思:“这样做是为了防止具有共同名称的目录(例如字符串)无意间隐藏稍后在模块搜索路径中出现的有效模块”?
卡尔·G

97
@CarlG Python搜索目录列表以解析例如import语句中的名称。因为这些目录可以是任何目录,并且最终用户可以添加任意目录,所以开发人员必须担心目录与一个有效的Python模块(例如docs示例中的“ string”)共享一个名称。为了减轻这种情况,它会忽略不包含名为_ _ init _ _ _.py(无空格)的文件的目录,即使该文件为空。
两位炼金术士2014年

186
@CarlG试试这个。创建一个名为“ datetime”的目录,并在其中创建两个空白文件,即init.py文件(带有下划线)和datetime.py。现在打开一个解释器,导入sys,然后发出sys.path.insert(0, '/path/to/datetime'),将该路径替换为您刚才创建的任何目录的路径。现在尝试类似from datetime import datetime;datetime.now()。您应该得到一个AttributeError(因为它现在正在导入空白文件)。如果要重复这些步骤而不创建空白的init文件,则不会发生这种情况。这就是要防止的。
两位炼金术士

4
@DarekNędza如果您不能仅打开Python解释器并正确执行命令,则您的设置有误from datetime import datetime。从版本2.3开始一直很好!
2位炼金术士

5
@SWang:这是不正确的:builtins列出了内置函数,而不是内置模块(请参阅docs.python.org/3/tutorial/modules.html#the-dir-function)。如果要列出内置模块,请执行import sys; print(sys.builtin_module_names)(cf. docs.python.org/3/library/sys.html#sys.builtin_module_names)。
Maggyero '18

842

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

mydir/spam/__init__.py
mydir/spam/module.py

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

import spam.module

要么

from spam import module

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

__init__.py文件通常为空,但可用于以更方便的名称导出包的选定部分,保留方便的功能等。给定上面的示例,可以按以下方式访问init模块的内容:

import spam

基于


96
更新:该文件__init__.py在Python 2.X下是必需的,在Python 2.7.12(我已经对其进行测试)中仍然是必需的,但是从(据说)Python 3.3开始不再需要该文件,并且在Python 3.4.3(我已经不再需要)测试过)。有关更多详细信息,请参见stackoverflow.com/questions/37139786
Rob_before_edits

4
不要使用它。它是“命名空间”包,而不是常规包。名称空间包用于非常罕见的用例。您可能不需要知道何时使用它。只需使用__init__.py
甲烷

2
但是,如果您拥有setup.py并使用find_packages()它,则必须__init__.py在每个目录中都有。参见stackoverflow.com/a/56277323/7127824
techkuz,

483

除了将目录标记为Python软件包并定义之外__all__,还__init__.py允许您在软件包级别定义任何变量。如果程序包定义了将以类似于API的方式频繁导入的内容,则这样做通常很方便。这种模式促进了对Pythonic的“扁平优于嵌套”哲学的坚持。

一个例子

这是我的一个项目的示例,在该项目中,我经常导入sessionmaker被叫Session以与数据库交互。我写了一个带有一些模块的“数据库”包:

database/
    __init__.py
    schema.py
    insertions.py
    queries.py

__init__.py包含以下代码:

import os

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

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

既然我Session在这里定义,就可以使用以下语法开始新的会话。此代码将从“数据库”包目录的内部或外部执行相同。

from database import Session
session = Session()

当然,这是一个小方便—替代方法是Session在数据库包中的新文件(例如“ create_session.py”)中定义,然后使用以下命令启动新会话:

from database.create_session import Session
session = Session()

进一步阅读

有一个非常有趣的reddit线程,涵盖了__init__.py此处的适当用法:

http://www.reddit.com/r/Python/comments/1bbbwk/whats_your_opinion_on_what_to_include_in_init_py/

大多数人似乎认为__init__.py文件应该非常薄,以避免违反“显式优于隐式”的哲学。


3
enginesessionmakercreate_engine,并且os都可以也可以从进口的database,现在......好像你已经取得了该命名空间的混乱。
ArtOfWarfare 2015年

9
@ArtOfWarfare,您可以__all__ = [...]用来限制通过导入的内容import *。但是除此之外,是的,您还有一个凌乱的顶级名称空间。
内森·古尔德

我可以知道什么是“数据库URL”吗?我试图通过用'mysql + mysqldb:// root:python @ localhost:3306 / test'括起来的create_engine来复制它,但是它不起作用。谢谢。
SunnyBoiz

2
您将如何从包内部(例如quieries.py)访问init中定义的“会话”类?
vldbnc

252

有两个主要原因 __init__.py

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

    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()

    from your_package import add

    不知道file1,例如

    from your_package.file1 import add
  2. 如果您想初始化一些东西;例如,日志记录(应放在顶层):

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

6
哦,在阅读答案之前,我认为从其位置显式调用一个函数是一个好习惯。
艾林

2
@Aerin最好不要认为简短的陈述(或者在这种情况下是主观结论)总是正确的。从导入__init__.py有时可能有用,但并非始终如此。
Tobias Sette

2
这些代码是在导入时还是在运行时执行?
user1559897

111

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

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


89

从Python 3.3开始,__init__.py不再需要将目录定义为可导入的Python包。

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

对不需要__init__.py标记文件并且可以自动跨越多个路径段的软件包目录的本地支持(受PEP 420中所述的各种第三方方法启发,用于命名空间软件包)

这是测试:

$ 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-packages https://www.python.org/dev/peps/pep-0420/
是__init__。 py对于Python 3中的软件包不是必需的?


3
它是“命名空间”包。不要将其用于常规包装。
甲烷

@methan,您能详细说明一下吗?
罗伯特·拉格

3
@RobertLugg参见dev.to/
甲烷/

57

在Python中,包的定义非常简单。像Java一样,层次结构和目录结构相同。但是您必须将__init__.py其打包。我将__init__.py用以下示例解释该文件:

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中添加一个函数:

def function_X():
    print "function_X in module_n1"
    return

运行后:

>>>from package_x.subPackage_b.module_n1 import function_X
>>>function_X()

function_X in module_n1 

然后,我们遵循层次结构包,并将module_n1称为函数。我们可以__init__.py像这样在subPackage_b中使用:

__all__ = ['module_n2', 'module_n3']

运行后:

>>>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

因此,使用*导入,模块包受__init__.py内容的约束。


我的setup.py看起来如何通过打包的库进行相同的导入? from package_x.subPackage_b.module_n1 import function_X
technazi

所以这里要取消的关键是“使用*导入,模块包受init .py内容的约束”
Minnie Shi

54

尽管Python在没有__init__.py文件的情况下仍可工作,但您仍应包含一个文件。

它指定应将程序包视为模块,因此将其包括在内(即使它为空)。

在某些情况下,您实际上可能会使用__init__.py文件:

假设您具有以下文件结构:

main_methods 
    |- methods.py

methods.py包含以下内容:

def foo():
    return 'foo'

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

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__.pyfoo()只需导入即可使用main_methods

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

这是有效的,因为__init__.py它被视为包装的一部分。


一些Python软件包实际上是这样做的。以JSON为例,其中running import json实际上是__init__.pyjson包中导入的(请参阅此处的包文件结构):

源代码: Lib/json/__init__.py


39

__init__.py 会将其所在目录视为可加载模块。

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

$ 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
>>> 

30

它有助于导入其他python文件。当您将此文件放置在包含其他py文件的目录中(例如,东西)时,可以执行诸如import stuff.other之类的操作。

root\
    stuff\
         other.py

    morestuff\
         another.py

如果__init__.py在目录东西中没有此内容,则无法导入other.py,因为Python不知道东西的源代码在哪里,也无法将其识别为包。


2
我在我的项目(python 3.4)中具有相同的结构,但是我无法使another.py看到other.py。我应该如何导入?从root.stuff导入其他?它可以在VSCode调试模式下工作,但不能在命令行中工作。有任何想法吗?
rodrigorf

10

一个__init__.py文件使得进口容易。当__init__.py包中包含an时,a()可以从文件中导入函数,b.py如下所示:

from b import a

但是,没有它,您将无法直接导入。您必须修改系统路径:

import sys
sys.path.insert(0, 'path/to/b.py')

from b import a

您的意思是“ 可以从文件b.py [snippet]中导入函数a(),但是,如果没有它,您将无法直接导入。 ”?我可以从文件b.py中导入函数a()而不使用__init__.py。
aderchox
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.