【发布时间】:2011-09-01 09:48:04
【问题描述】:
我正在创建一个 Python 模块,将不同语言/框架提供的 API 映射到 Python 中。理想情况下,我希望将其呈现为单个根包,该包公开辅助方法,并将该其他框架中的所有命名空间映射到 Python 包/模块。为了方便,我们以CLR为例:
import clr.System.Data
import clr.System.Windows.Forms
这里 clr 是一个神奇的顶级包,它公开了 CLR 命名空间 System.Data 和 System.Windows.Forms 子包/子模块(据我所知,一个包只是一个带有子模块/包的模块;它是其他类型的成员仍然有效)。
我已经阅读了PEP-302 并编写了一个简单的原型程序,通过安装自定义的meta_path 挂钩来实现类似的效果。 clr 模块本身是一个合适的 Python 模块,在导入时会设置 __path__ = [](使其成为一个包,因此 import 甚至会尝试查找子模块),并注册钩子。钩子本身会拦截任何包的全名以"clr." 开头的包加载,使用imp.new_module() 动态创建新模块,将其注册到sys.modules,并使用精灵灰尘和彩虹填充它的类和方法原始 API。代码如下:
clr.py
import sys
import imp
class MyLoader:
def load_module(self, fullname):
try:
return sys.modules[fullname]
except KeyError:
pass
print("--- load ---")
print(fullname)
m = imp.new_module(fullname)
m.__file__ = "clr:" + fullname
m.__path__ = []
m.__loader__ = self
m.speak = lambda: print("I'm " + fullname)
sys.modules.setdefault(fullname, m)
return m
class MyFinder:
def find_module(self, fullname, path = None):
print("--- find ---")
print(fullname)
print(path)
if fullname.startswith("clr."):
return MyLoader()
return None
print("--- init ---")
__path__ = []
sys.meta_path.append(MyFinder())
test.py
import clr.Foo.Bar.Baz
clr.Foo.speak()
clr.Foo.Bar.speak()
clr.Foo.Bar.Baz.speak()
总而言之,这一切似乎都很好。 Python 保证链中的模块是从左到右导入的,所以clr 总是首先导入,它设置了允许导入链的其余部分的钩子。
但是,我想知道我在这里所做的是否是矫枉过正。毕竟,我正在安装一个全局钩子,任何模块导入都会调用它,即使我过滤掉了我不关心的那些。是否有某种方法可以安装一个只为从我的特定包导入而不是其他包调用的钩子?或者上面是在 Python 中做这种事情的正确方法吗?
【问题讨论】:
-
我确定您的代码只是一个示例,但只是为了检查一下——如果您真的希望将 CLR 库移植到 Python,您应该使用 IronPython。 =)
-
@katrielalex:确实,我非常了解 IronPython。应该注意的是,它并没有完全解决您提到的问题 - 即,它要求您将 您的 Python 代码 移植到 CLR,而不是使用 CPython 中的 CLR 库(例如,与一些 C 库一起使用) )。但是,是的,在这种情况下,CLR 仅用作具体的、易于演示的示例;真正的东西是别的东西。
标签: python import-hooks