2010-04-23 75 views
42

我从未注意到今天之前在我的一些软件包中定义的__path__属性。根据文档:__path__对于什么有用?

软件包支持一个更特别的 属性,__path__。这是 已初始化为包含 的列表,其中包含 程序包的__init__.py之前的代码 的目录的名称被执行。这个 变量可以被修改;这样做 影响将来搜索模块 和包含在 包中的子包。

虽然此功能通常不需要 ,但它可用于扩展包中的 模块集。

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

回答

22

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

如果你把pkgutil.extend_path(__path__, __name__)/usr/lib/python2.6/site-packages/zope/__init__.py那么这两个zope.interface和zope.schema将导入的,因为pkgutil将不得不['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope']变化__path__

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

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

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... 
+1

我有一种感觉,它与命名空间包有关,但我在拼凑它的工作方式时遇到了问题。谢谢! – 2010-04-27 20:36:03

28

如果更改__path__,您可以强制解释在不同的目录去寻找属于那个包的模块。

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

+0

Django使用这个在启动时动态地加载django-admin.py命令! '命令=​​ {名称:'django.core'在find_commands(__ path __ [0])中的名称}} – 2014-05-10 18:46:23

7

除了选择不同版本的基础上运行时条件模块的句法说,这个功能也可以让您将包分解成多个部分/下载/同时保持单个逻辑封装的外观安装。

请考虑以下情况。

  • 我有两个包,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是提供给他们。如果他们没有,它不存在。

0

具体情况我遇到是当一个包:

您还可以使用__path__为的monkeypatching,例如,我已经通过创建一个文件distutils/__init__.py,早期是sys.path monkeypatched在时间的distutils变得足够大,我想将它的部分分割成子目录,而不必更改任何引用它的代码。

例如,我有一个名为views的软件包,它正在收集许多支持的实用程序函数,这些函数与包的主要顶级目的混淆。我能够把这些配套功能移进子目录utils并添加以下行__init__.pyviews包:

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

随着这一变化也views/__init_.py,我可以在新文件运行该软件的其余部分结构,而不需要对文件做进一步的修改。

(我试图做的views/__init__.py文件import语句类似的东西,但子包模块仍然不可见通过view包的进口 - 我不能完全肯定,如果我想的东西有;上欢迎评论)

(基于Python 2.7版安装)这种反应