__path__ 有啥用?

Posted

技术标签:

【中文标题】__path__ 有啥用?【英文标题】:What is __path__ useful for?__path__ 有什么用? 【发布时间】:2011-02-11 13:26:12 【问题描述】:

在今天之前,我从未注意到在我的一些包上定义的 __path__ 属性。根据文档:

包支持另一种特殊的 属性,__path__。这是 初始化为一个列表,包含 存放目录的名称 包的__init__.py在代码前 在该文件中执行。这 变量可以修改;这样做 影响未来对模块的搜索 和包含在 包。

虽然此功能并不常见 需要时,它可以用来扩展 包中的一组模块。

有人可以向我解释这到底是什么意思以及我为什么要使用它吗?

【问题讨论】:

【参考方案1】:

这通常与pkgutil 一起使用,让包在磁盘上布局。例如,zope.interface 和 zope.schema 是独立的发行版(zope 是一个“命名空间包”)。您可能在 /usr/lib/python2.6/site-packages/zope/interface/ 中安装了 zope.interface,而您在 /home/me/src/myproject/lib/python2.6/site-packages/zope/schema 中更多地在本地使用 zope.schema。

如果您将pkgutil.extend_path(__path__, __name__) 放入/usr/lib/python2.6/site-packages/zope/__init__.py,那么 zope.interface 和 zope.schema 都将是可导入的,因为 pkgutil 会将 __path__ 更改为 ['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope']

pkg_resources.declare_namespace(Setuptools 的一部分)类似于pkgutil.extend_path,但更清楚路径上的 zip。

手动更改__path__ 并不常见,也可能没有必要,但在调试命名空间包的导入问题时查看变量很有用。

您也可以使用__path__ 进行monkeypatching,例如,我有时通过在sys.path 早期创建一个文件distutils/__init__.py 来对distutils 进行monkeypatched:

import os
stdlib_dir = os.path.dirname(os.__file__)
real_distutils_path = os.path.join(stdlib_dir, 'distutils')
__path__.append(real_distutils_path)
execfile(os.path.join(real_distutils_path, '__init__.py'))
# and then apply some monkeypatching here...

【讨论】:

我感觉它与命名空间包有关,但我在拼凑它的工作原理时遇到了问题。谢谢!【参考方案2】:

如果您更改__path__,您可以强制解释器在不同的目录中查找属于该包的模块。

这将允许您根据运行时条件加载同一模块的不同版本。如果您想在不同平台上使用相同功能的不同实现,您可以这样做。

【讨论】:

django 使用它在启动时动态加载 django-admin.py 命令。 commands = name: 'django.core' for name in find_commands(__path__[0])【参考方案3】:

除了像Syntactic 所说的那样根据运行时条件选择不同版本的模块之外,此功能还允许您将包分成多个部分/下载/安装,同时保持单个逻辑包的外观。

考虑以下问题。

我有两个包,mypkg_mypkg_foo_mypkg_foo 包含mypkgfoo.py 的可选模块。 下载和安装后,mypkg 不包含foo.py

mypkg__init__.py 可以这样做:

try:
    import _mypkg_foo
    __path__.append(os.path.abspath(os.path.dirname(_mypkg_foo.__file__)))
    import mypkg.foo
except ImportError:
    pass

如果有人安装了包_mypkg_foo,那么他们可以使用mypkg.foo。如果他们没有,它就不存在。

【讨论】:

这里import mypkg.foo有什么意义?【参考方案4】:

我遇到的一种特殊情况是,当一个包变得足够大时,我想将它的一部分拆分到子目录中,而无需更改任何引用它的代码。

例如,我有一个名为views 的包,它收集了许多与包的主要***用途混淆的支持实用功能。我能够将这些支持功能移动到子目录 utils 中,并将以下行添加到 views 包的 __init__.py 中:

__path__.append(os.path.join(os.path.dirname(__file__), "utils"))

也有了这个更改views/__init_.py,我可以使用新的文件结构运行软件的其余部分,而无需对文件进行任何进一步的更改。

(我尝试对 views/__init__.py 文件中的 import 语句执行类似的操作,但是通过导入 view 包仍然看不到子包模块 - 我不完全确定我是否我错过了一些东西;欢迎 cmets!)

(此响应基于 Python 2.7 安装)

【讨论】:

以上是关于__path__ 有啥用?的主要内容,如果未能解决你的问题,请参考以下文章

虚拟子类有啥用?

C中__IO和静态关键字有啥用?

Kafka 中的 __consumer_offsets 和 _schema 主题有啥用?

在编写设备驱动程序时,linux中的__iomem有啥用?

Python中__init__()和self的有啥用

python中的os.path.splitext是干啥用的