python - 在 python-3.4+ 中实现导入后挂钩的正确方法
问题描述
我想修改导入行为。我在 David Beazley 和 Brian K. Jones 的“Python Cookbook”一书中找到了一个带有 post import hook 的示例,它应该适合我的问题。
由于在python-3.3
提供示例的时间发布了最新版本,因此已过时。我不得不自己修改它,以便代码与importlib
after兼容python-3.4
。在原始版本中定义了PostImportFinder
class而不是find_module(self, fullname, path = None)
定义了 in 。这是一个可重现的小例子:PostImportLoader
create_module()
load_module()
#postimport.py
import
import importlib
import sys
from collections import defaultdict
_post_import_hooks = defaultdict(list)
class PostImportFinder:
def __init__(self):
self._skip=set()
def find_spec(self, fullname, path = None, target = None):
if fullname in self._skip:
return None
self._skip.add(fullname)
return PostImportLoader(self)
class PostImportLoader:
def __init__(self, finder):
self._finder = finder
def create_module(self, spec):
importlib.import_module(spec.name)
module = sys.modules[spec.name]
for func in _post_import_hooks[spec.name]:
func(module)
self._finder._skip.remove(spec.name)
return module
def when_imported(names):
def decorate(func):
for fullname in names:
if fullname in sys.modules:
print(f'importing {fullname}')
func(sys.modules[fullname])
else:
_post_import_hooks[fullname].append(func)
return func
return decorate
sys.meta_path.insert(0,PostImportFinder)
#postimportfunc.py
from inspect import getmembers, isfunction, isclass
from postimport import when_imported
list_of_module_names = ['simple']
#Some decorator. For example purposes pretty simple
@when_imported(list_of_module_names)
def decorate(mod):
# Decorate classes
print(f'module {mod} imported')
#simple.py
class A:
def __init__(self):
self.x=42
def bar(self):
print(self.x)
#start.py
import postimportfunc
from simple import A
foo = A()
foo.bar()
当我运行start.py
时出现以下错误:
runfile('/home/user/reproduce/start.py', wdir='/home/user/reproduce')
File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 827, in runfile
execfile(filename, namespace)
File "/usr/lib/python3/dist-packages/spyder_kernels/customize/spydercustomize.py", line 110, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "/home/user/reproduce/start.py", line 2, in <module>
import postimportfunc
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 971, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 914, in _find_spec
File "/home/user/reproduce/postimport.py", line 12, in find_spec
if fullname in self._skip:
AttributeError: 'str' object has no attribute '_skip'
所以我的问题很明显:我做错了什么?我根本看不到_skip
和str
是如何相关的,因为我初始化_skip
为一个集合。是否有另一种/更好的方法来更改指定模块的导入后行为?
更新: PS 我忘了提,我目前使用 python-3.7 。
解决方案
主要原因是最后一行中的一个愚蠢的错字postimport.py
。代替:
sys.meta_path.insert(0,PostImportFinder)
它应该是:
sys.meta_path.insert(0,PostImportFinder()) #DO NOT FORGET PARENTHESIS
但这不是唯一的问题。我还必须更改以下内容:
在PostImportFinder
重命名find_spec(self, fullname, path = None, targer = None)
为find_module(self, fullname, path = None)
并PostImportLoader
替换find_spec()
为:
def load_module(self, fullname):
importlib.import_module(fullname)
module = sys.modules[fullname]
for func in _post_import_hooks[fullname]:
func(module)
self._finder._skip.remove(fullname)
return module
所以,总结一下:我应该坚持问题中提到的书中的食谱。
推荐阅读
- amazon-web-services - java.net.UnknownHostException Spring Boot AWS EC2 Apache Kafka
- dart - Dart 中何时以及如何释放资源?
- css - 一些响应式 CSS 问题
- eclipse - 签入 selenium java 代码不起作用
- firebase - 显示函数中的文本值
- sql - 仅使用已编辑行更新另一个表的触发器
- javascript - 有没有办法将私人频道添加到 twilio 可编程聊天中?
- c# - 为文件夹分支中的所有类启用 C# 8.0 的可空引用类型功能
- python - python pandas用iterrows迭代数据帧行很慢,可以以某种方式替换吗?
- java - 从 java 命令行应用程序中读取 cmd 的 cd