什么是__path__有用?

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

包支持一个更多的特殊属性, __path__ 。 在执行该文件中的代码之前,它被初始化为包含包含__init__.py的目录名称的列表。 这个variables可以修改; 这样做会影响将来对包中包含的模块和子包的search。

虽然此function通常不是必需的,但它可用于扩展包中的模块组。

有人可以向我解释这是什么意思,为什么我会想用它?

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

如果在/usr/lib/python2.6/site-packages/zope/__init__.py放入pkgutil.extend_path(__path__, __name__) ,那么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_namespacepkg_resources.declare_namespace一部分)与pkgutil.extend_path类似,但更注意path上的拉链。

手动更改__path__并不常见,也可能不是必需的,不过在debugging命名空间包导入问题时查看variables很有用。

你也可以使用__path__来进行monkeypatching,例如,我有时通过在sys.path上创build一个distutils/__init__.py文件来避免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... 

如果更改了__path__ ,则可以强制解释器查看属于该包的模块的其他目录。

这将允许您基于运行时条件加载相同模块的不同版本。 如果你想在不同平台上使用相同function的不同实现,你可能会这样做。

按照Syntactic的说法,除了根据运行时间条件select不同版本的模块之外,这个function还允许您在保持单个逻辑包的外观的同时,将包分解为多个部分/下载/安装。

考虑以下。

  • 我有两个包, 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就可以使用。 如果他们没有,它不存在。

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

例如,我有一个名为views的软件包,它收集了一些支持的实用程序函数,这些函数正在被软件包的主要顶级目的弄糊涂了。 我能够将这些支持函数移动到子目录utils ,并将以下行添加到views包的__init__.py

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

通过这个改变,我也可以用新的文件结构运行剩下的软件,而不需要对文件做任何修改。

(我尝试在views/__init__.py文件中使用import语句做类似的事情,但是通过导入view包,子包模块仍然不可见 – 我不完全确定是否在那里丢失了某些东西;对此表示欢迎!)

(这个基于Python 2.7安装的响应)