__path__对什么有用?

时间:2010-04-23 14:22:27

标签: python path module

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

  

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

     

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

有人可以向我解释这究竟是什么意思以及为什么我会想要使用它?

4 个答案:

答案 0 :(得分:31)

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

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

答案 1 :(得分:30)

这通常与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类似,但更了解路径上的拉链。

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

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

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 :(得分:7)

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

请考虑以下事项。

  • 我有两个包,mypkg_mypkg_foo
  • _mypkg_foo包含mypkgfoo.py的可选模块。
  • 已下载并安装,mypkg不包含foo.py

mypkg的{​​{1}}可以这样做:

__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。如果没有,则不存在。

答案 3 :(得分:0)

我遇到的一个特殊情况是当一个包变得足够大以至于我想将它的一部分拆分成子目录而不必更改引用它的任何代码。

例如,我有一个名为views的软件包正在收集许多支持实用程序功能,这些功能因包的主要顶层目的而变得混乱。我能够将这些支持函数移动到子目录utils中,并将以下行添加到__init__.py包的views中:

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

通过此更改views/__init_.py,我可以使用新文件结构运行其余软件,而无需对文件进行任何进一步更改。

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

(此响应基于Python 2.7安装)