什么是__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_namespace
( pkg_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
包含可选模块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
就可以使用。 如果他们没有,它不存在。
我遇到的一个特殊情况是,当一个包变得足够大,我想把它的一部分拆分成子目录,而不必修改任何引用它的代码。
例如,我有一个名为views
的软件包,它收集了一些支持的实用程序函数,这些函数正在被软件包的主要顶级目的弄糊涂了。 我能够将这些支持函数移动到子目录utils
,并将以下行添加到views
包的__init__.py
:
__path__.append(os.path.join(os.path.dirname(__file__), "utils"))
通过这个改变,我也可以用新的文件结构运行剩下的软件,而不需要对文件做任何修改。
(我尝试在views/__init__.py
文件中使用import
语句做类似的事情,但是通过导入view
包,子包模块仍然不可见 – 我不完全确定是否在那里丢失了某些东西;对此表示欢迎!)
(这个基于Python 2.7安装的响应)