__init__. py有什么用?

Python源目录中的#0是什么?

2029431 次浏览

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

这是留档。

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

但只需单击链接,它包含一个示例、更多信息和命名空间包的解释,即没有__init__.py的包。

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

root\stuff\other.py
morestuff\another.py

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

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

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

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

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

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

import spam.module

from spam import module

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

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

import spam

基于这个

除了将目录标记为Python包并定义__all__之外,如果包定义了将以类似API的方式频繁导入的内容,则这样做通常很方便。这种模式促进了对Pythonic“扁平优于嵌套”哲学的坚持。

一个例子

这是我的一个项目的示例,其中我经常导入一个名为Sessionsessionmaker来与我的数据库交互。我编写了一个包含几个模块的“数据库”包:

database/__init__.pyschema.pyinsertions.pyqueries.py

我的__init__.py包含以下代码:

import os
from sqlalchemy.orm import sessionmakerfrom sqlalchemy import create_engine
engine = create_engine(os.environ['DATABASE_URL'])Session = sessionmaker(bind=engine)

由于我在这里定义了Session,我可以使用下面的语法启动一个新会话。这段代码将在“数据库”包目录内部或外部执行。

from database import Sessionsession = Session()

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

from database.create_session import Sessionsession = Session()

进一步阅读

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

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

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

在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函数。我们可以在subPackage_b中使用__init__.py,如下所示:

__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内容。

__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 moduleTraceback (most recent call last):File "<stdin>", line 1, in <module>ImportError: No module named spam>>>

__init__.py有两个主要原因

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

    your_package/__init__.pyfile1.pyfile2.py...fileN.py
    # in __init__.pyfrom .file1 import *from .file2 import *...from .fileN import *
    # in file1.pydef add():pass

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

     from your_package import add

    不知道file1的内部函数,比如

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

     import logging.configlogging.config.dictConfig(Your_logging_config)

从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/
Python 3中的包不需要__init__. py吗?

尽管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__.py,那么您可以通过导入main_methods来使用foo()

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

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


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

源代码:Lib/json/__init__.py

__init__.py文件使导入变得容易。当包中存在__init__.py时,可以像这样从文件b.py导入函数a()

from b import a

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

import syssys.path.insert(0, 'path/to/b.py')
from b import a

__init__. py允许的一件事是将模块转换为包,而无需破坏API或创建无关的嵌套命名空间或私有模块*。

如果我有一个文件util.py包含

def foo():...

然后用户将访问foo

from util import foo

如果我想为数据库交互添加实用函数,并且我希望它们在util下拥有自己的命名空间,我需要一个新目录**,并保持API兼容性(以便from util import foo仍然有效),我会将其命名为util/。我可以将util.py移动到util/中,就像这样,

util/__init__.pyutil.pydb.py

在util/__init__. py中

from util import *

但这是多余的。我们可以将util.py内容放在__init__. py中,而不是使用util/util.py文件,用户现在可以

from util import foofrom util.db import check_schema

我认为这很好地突出了util包的__init__. py的行为方式与util模块相似

*这在其他答案中有所暗示,但我想在这里强调一下
**没有使用导入体操。请注意,创建与文件同名的新包不起作用,请参阅这个

init.py:这是一个python文件,位于包目录中,当导入包或包中的模块时调用它。您可以使用它来执行包初始化代码,即每当导入包时,都会先执行python语句,然后再执行该文件夹中的其他模块。它类似于c或java程序的main函数,但它存在于python包模块(文件夹)而不是核心python文件中。它还可以访问这个init.py文件中定义的全局变量,就像模块导入python文件一样。

例如。我在一个名为pymodlib的文件夹中有一个init.py文件,该文件包含以下语句:

print(f'为{姓名}调用init.py')/p>for_loop,while__loop

当我在我的解决方案模块或笔记本或python控制台中导入此包“pymodlib”时:这两个语句在导入时执行。因此,在日志或控制台中,您会看到以下输出:

导入pymodlib为pymodlib调用init.py

在python控制台的下一条语句中:我可以访问全局变量:

pymodlib.pystructures输出如下:

['for_loop','while__loop','如果条件']

现在从python3.3开始,使用此文件是可选的,以使文件夹成为python模块。所以你跳过了将其包含在python模块文件夹中。

如果您使用的是Python 2并且想要加载文件的兄弟文件,您可以简单地将文件的父文件夹添加到会话的系统路径中。它的行为与当前文件是初始化文件的行为大致相同。

import osimport sysdir_path = os.path.dirname(__file__)sys.path.insert(0, dir_path)

之后,相对于文件目录的常规导入将正常工作。例如。

import cheesefrom vehicle_parts import *# etc.

通常情况下,您希望使用正确的init.py文件,但是在处理遗留代码时,您可能会陷入f.ex.库硬编码以加载特定文件的困境。对于这些情况,这是一种替代方案。