首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在python-3.4+中实现后导入钩子的正确方法

在python-3.4+中实现后导入钩子的正确方法
EN

Stack Overflow用户
提问于 2021-10-17 20:28:03
回答 1查看 25关注 0票数 0

我想修改导入行为。我在David Beazley和Brian K.Jones的"Python Cookbook“一书中找到了一个post导入钩子的例子,它应该适合我的问题。

由于最新版本是在python-3.3时代发布的,因此提供的示例已过时。我不得不自己修改它,以便代码在python-3.4之后与importlib兼容。在最初的版本中,在PostImportFinder中定义了find_module(self, fullname, path = None)类,在PostImportLoader中定义了load_module(),而不是create_module()。下面是一个可重复使用的小示例:

代码语言:javascript
复制
#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)
代码语言:javascript
复制
#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')
代码语言:javascript
复制
#simple.py
class A:
    def __init__(self):
        self.x=42
    
    def bar(self):
        print(self.x)
代码语言:javascript
复制
#start.py
import postimportfunc
from simple import A


foo = A()
foo.bar()

当我运行start.py时,出现以下错误:

代码语言:javascript
复制
    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' 

所以我的问题很明显:我做错了什么?我根本看不出_skipstr有什么关系,因为我把_skip初始化为一个集合。有没有其他/更好的方法来改变指定模块的移植后行为?

更新:另外,我忘了提一下,我目前使用的是python-3.7。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-17 22:05:51

主要原因是postimport.py最后一行中的一个愚蠢的打字错误。而不是:

代码语言:javascript
复制
sys.meta_path.insert(0,PostImportFinder)

它应该是:

代码语言:javascript
复制
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()替换为:

代码语言:javascript
复制
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

所以,总结一下:我应该坚持问题中提到的书中的食谱。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69608322

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档