我希望能够导入一个实际上位于另一个模块的子目录中的 python 模块。
我正在开发一个带有插件的框架。因为我希望有几千个(目前已经 > 250 个)并且我不想要一个包含 > 1000 个文件的大目录,所以我将它们排序在这样的目录中,它们按名称的第一个字母分组:
framework\
__init__.py
framework.py
tools.py
plugins\
__init__.py
a\
__init__.py
atlas.py
...
b\
__init__.py
binary.py
...
c\
__init__.py
cmake.py
...
由于我不想给其他插件的开发人员或不需要像我一样多的人带来负担,我想将每个插件放在“framework.plugins”命名空间中。这样,添加一堆私有插件的人只需将它们添加到文件夹 framework.plugins 中即可,并提供一个包含以下内容的__init__.py
文件:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
然而,目前这种设置迫使他们也使用 az 子目录。有时一个插件正在扩展另一个插件,所以现在我有一个
from framework.plugins.a import atlas
我想拥有
from framework.pugins import atlas
有没有办法声明一个名称空间,其中完整的名称空间名称实际上不映射到文件夹结构?
我知道 pkg_resources 包,但这只能通过 setuptools 获得,我宁愿没有额外的依赖。
import pkg_resources
pkg_resources.declare_namespace(__name__)
该解决方案应适用于 python 2.4-2.7.3
更新:结合提供的答案,我试图获取__init__.py
从插件中导入的所有插件的列表。但是,由于依赖关系,这会失败。由于“c”文件夹中的插件尝试导入以“t”开头的插件,但尚未添加此插件。
plugins = [ x[0].find_module(x[1]).load_module(x[1]) for x in pkgutil.walk_packages([ os.path.join(framework.plugins.__path__[0], chr(y)) for y in xrange(ord('a'), ord('z') + 1) ],'framework.plugins.' ) ]
我不确定我是否在正确的轨道上,或者只是让事情变得过于复杂并更好地编写我自己的 PEP302 导入器。但是,我似乎找不到任何合适的例子来说明这些应该如何工作。
更新:我尝试按照将__getattr__
函数包装在 my 中的建议进行操作__init__.py
,但这似乎无济于事。
import pkgutil
import os
import sys
plugins = [x[1] for x in pkgutil.walk_packages([ os.path.join(__path__[0], chr(y)) for y in xrange(ord('a'), ord('z') + 1) ] )]
import types
class MyWrapper(types.ModuleType):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
if name in plugins:
askedattr = name[0] + '.' + name
else:
askedattr = name
attr = getattr(self.wrapped, askedattr)
return attr
sys.modules[__name__] = MyWrapper(sys.modules[__name__])