__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
包含mypkg
、foo.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__ 有啥用?的主要内容,如果未能解决你的问题,请参考以下文章