[英]What is __init__.py for?
它曾经是包的必需部分( 旧的,3.3 之前的“常规包” ,而不是较新的 3.3+“命名空间包” )。
Python 定义了两种类型的包,常规包和命名空间包。 常规包是 Python 3.2 及更早版本中存在的传统包。 常规包通常实现为包含
__init__.py
文件的目录。 当一个常规包被导入时,这个__init__.py
文件被隐式执行,它定义的对象被绑定到包的命名空间中的名称。__init__.py
文件可以包含任何其他模块可以包含的相同 Python 代码,并且 Python 会在导入模块时为其添加一些附加属性。
但只需单击链接,它包含一个示例、更多信息和对命名空间包的解释,即没有__init__.py
的包类型。
名为__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
基于此
除了将目录标记为 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()
当然,这是一个小小的便利——替代方法是在我的数据库包中的“create_session.py”之类的新文件中定义Session
,并使用以下命令启动新会话:
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
文件应该非常薄,以避免违反“显式优于隐式”的理念。
__init__.py
有两个主要原因
为方便起见:其他用户不需要知道您的函数在包层次结构(文档)中的确切位置。
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
如果你想初始化一些东西; 例如,日志记录(应该放在顶层):
import logging.config logging.config.dictConfig(Your_logging_config)
__init__.py
文件使 Python 将包含它的目录视为模块。
此外,这是要在模块中加载的第一个文件,因此您可以使用它来执行每次加载模块时要运行的代码,或指定要导出的子模块。
从 Python 3.3 开始,不再需要__init__.py
将目录定义为可导入的 Python 包。
对不需要
__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_methods
print(main_methods.foo()) # Prints 'foo'
这是有效的,因为__init__.py
被视为包的一部分。
一些 Python 包实际上是这样做的。 以JSON为例,运行import json
实际上是从json
包中导入__init__.py
(请参阅此处的包文件结构):
源代码:
Lib/json/__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 module
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named spam
>>>
它有助于导入其他 python 文件。 当您将此文件放在包含其他 py 文件的目录(例如东西)中时,您可以执行诸如 import stuff.other 之类的操作。
root\
stuff\
other.py
morestuff\
another.py
如果目录 stuff 中没有这个__init__.py
,则无法导入 other.py,因为 Python 不知道 stuff 的源代码在哪里,并且无法将其识别为包。
__init__.py
文件使导入变得容易。 当包中存在__init__.py
时,可以从文件b.py
中导入函数a()
,如下所示:
from b import a
但是,没有它,您将无法直接导入。 您必须修改系统路径:
import sys
sys.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__.py
util.py
db.py
并在 util/__init__.py 做
from util import *
但这是多余的。 我们可以将 util.py 内容放在 __init__.py 中,而不是使用 util/util.py 文件,用户现在可以
from util import foo
from util.db import check_schema
我认为这很好地突出了util
包的 __init__.py 如何以类似于util
模块的方式运行
* 这在其他答案中有所暗示,但我想在这里突出显示
** 没有使用进口体操。 请注意,创建与文件同名的新包将不起作用,请参阅此
如果您使用的是 Python 2 并且想要加载文件的兄弟姐妹,您只需将文件的父文件夹添加到会话的系统路径即可。 它的行为与您当前的文件是一个 init 文件大致相同。
import os
import sys
dir_path = os.path.dirname(__file__)
sys.path.insert(0, dir_path)
之后,相对于文件目录的常规导入将正常工作。 例如
import cheese
from vehicle_parts import *
# etc.
通常,您希望使用适当的init .py 文件,但在处理遗留代码时,您可能会被 f.ex 卡住。 硬编码以加载特定文件的库,仅此而已。 对于这些情况,这是一种替代方法。
init .py :它是在包目录中找到的python文件,在导入包或包中的模块时调用它。 您可以使用它来执行包初始化代码,即,每当导入包时,首先执行 python 语句,然后再执行此文件夹中的其他模块。 它类似于 c 或 java 程序的 main 函数,但它存在于 python 包模块(文件夹)而不是核心 python 文件中。 它还可以访问此init .py 文件中定义的全局变量,就像将模块导入 python 文件时一样。
例如。 我在一个名为 pymodlib 的文件夹中有一个init .py 文件,该文件包含以下语句:
print(f'Invoking init .py for { name }') pystructures = ['for_loop', 'while__loop', 'ifCondition']
当我在我的解决方案模块或笔记本或 python 控制台中导入这个包“pymodlib”时:这两个语句在导入时被执行。 因此,在日志或控制台中,您会看到以下输出:
导入 pymodlib 为 pymodlib 调用init .py
在 python 控制台的下一条语句中:我可以访问全局变量:
pymodlib.pystructures 它给出以下输出:
['for_loop', 'while__loop', 'ifCondition']
现在从 python3.3 开始,可以选择使用这个文件来使文件夹成为 python 模块。 因此,您跳过将其包含在 python 模块文件夹中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.