Python 3中的Post import hooks

jhiyze9q  于 2023-05-23  发布在  Python
关注(0)|答案(4)|浏览(105)

我想有一些回调运行每当一个特定的模块被导入。例如(使用一个不存在的假@imp.when_imported函数):

@imp.when_imported('numpy')
def set_linewidth(numpy):
    import shutil
    numpy.set_printoptions(linewidth=shutil.get_terminal_size()[0])

此功能在PEP 369: Post import hooks中设计,但由于以下原因而被撤回:
这个PEP已经被其作者撤回,因为在Python 3.3中迁移到importlib之后,很多详细的设计都不再有效。
但是importlib没有明确的解决方案。如何使用importlib实现post-import钩子?

qybjjes1

qybjjes11#

我会震惊地发现这是最好的方法。然而,从早期的python2.x版本开始,就支持了monkey patching __import__。我们可以在这里利用这一点:

try:
    import builtins  # python3.x
except ImportError:
    import __builtin__ as builtins  # python2.x
import sys
import collections

_builtin_import = builtins.__import__

def _my_import(name, globals=None, locals=None, fromlist=(), level=0):
    already_imported = name in sys.modules

    mod = _builtin_import(
        name,
        globals=globals,
        locals=locals,
        fromlist=fromlist,
        level=level)

    if not already_imported and name in _post_import_hooks:
        for hook in _post_import_hooks[name]:
            hook()
    return mod

builtins.__import__ = _my_import

_post_import_hooks = collections.defaultdict(list)

def on_import(name):
    def decorator(func):
        _post_import_hooks[name].append(func)
        return func
    return decorator

@on_import('numpy')
def print_hi():
    print('Hello Numpy')

print('before numpy')
import numpy
print('after numpy')

这个答案使得注册回调的注册表非常简单。装饰器只是注册函数,然后返回它。它不做任何花哨的检查(例如,检查模块是否已经加载),但可以很容易地扩展到这样做。
很明显,缺点是如果其他模块决定对__import__进行临时修补,那么您就不走运了--这个模块或其他模块都可能会崩溃。
我已经测试过了,它似乎可以在python2.x和python3.x上工作。

qni6mghb

qni6mghb2#

wrapt模块提供了一个实现。

观看有关wrapt的视频,包括以下功能:

不要认为wrapt的文档中提到了它。
一些博客文章在结尾:

但还是要谈谈。
wrapt有一个名为autowrapt的配套模块,它允许您使用此机制进行monkey修补,而无需更改应用程序代码本身来触发它。

wqnecbli

wqnecbli3#

另一种简单得多但有限的方法是检查是否已导入numpy。只有当在程序中的某个点上所有导入都完成了,但是根据配置或环境,可能导入了也可能没有导入像numpy这样的特定模块时,这才有效。如果你需要处理一个模块在程序后面的任何时候被导入,那么这将不起作用。

import sys
if 'numpy' in sys.modules:
    import numpy  # has already been done, so this is free now
    set_linewidth(numpy)
8yparm6h

8yparm6h4#

这个有用吗

import importlib

class ImportHook:

    def __init__(self, func):
        self.func = func
        self.module = None

    def __enter__(self):
        return self

    def get_module(self, module_name):
        self.module = importlib.import_module(module_name)
        return self.module

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.module is not None:
            self.func(self.module)

def set_linewidth(module):
    import shutil
    module.set_printoptions(linewidth=shutil.get_terminal_size()[0])

with ImportHook(set_linewidth) as hook:
    numpy = hook.get_module('numpy')

相关问题