我正在尝试在 Python 3 中实现一个“导入钩子”。该钩子应该为每个导入的类添加一个属性。(不是每个班级,但为了简化问题,我们假设如此。)
我有一个加载器定义如下:
import sys
class ConfigurableImports(object):
def find_module(self, fullname, path):
return self
def create_module(self, spec):
# ???
def exec_module(self, module):
# ???
sys.meta_path = [ConfigurableImports()]
Run Code Online (Sandbox Code Playgroud)
文档指出,从 3.6 开始,加载器必须同时实现create_module
和exec_module
。但是,文档也几乎没有说明应该如何实现这些,也没有示例。我的用例非常简单,因为我只加载 Python 模块,加载器的行为应该与默认行为几乎完全相同。
如果可以,我只会使用importlib.import_module
然后相应地修改模块内容;然而,由于 importlib 利用了 import 钩子,我得到了无限递归。
编辑:我也尝试过使用imp
模块的load_module
,但这已被弃用。
有没有什么简单的方法可以用导入钩子实现这个功能,还是我用错误的方式来实现这个功能?
恕我直言,如果您只需要更改模块,即在找到并加载后使用它,则无需实际创建一个完整的钩子来查找、加载和返回模块;只是补丁__import__
。
这可以通过几行轻松完成:
import builtins
from inspect import getmembers, isclass
old_imp = builtins.__import__
def add_attr(mod):
for name, val in getmembers(mod):
if isclass(val):
setattr(val, 'a', 10)
def custom_import(*args, **kwargs):
m = old_imp(*args, **kwargs)
add_attr(m)
return m
builtins.__import__ = custom_import
Run Code Online (Sandbox Code Playgroud)
在这里,__import__
由您的自定义导入替换,该导入调用原始导入__import__
以获取加载的模块,然后调用一个函数add_attr
,该函数在返回模块之前对模块中的类(使用getmembers
和isclass
来自inspect
)进行实际修改。
当然,这是在创建import
脚本时进行更改的方式。
您可以并且可能应该创建辅助功能,restore
并在需要时再次更改它,例如:
def revert(): builtins.__import__ = old_imp
def apply(): builtins.__import__ = custom_import
Run Code Online (Sandbox Code Playgroud)
上下文管理器也会使这个实现更清晰。
归档时间: |
|
查看次数: |
1653 次 |
最近记录: |