在Python 3中发布导入钩子

Mar*_*ato 8 python python-import python-3.x

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

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

此功能是在PEP 369中设计的:后导入钩子但被撤回的原因是:

此PEP已被其作者撤回,因为在Python 3.3中迁移到importlib后,大部分详细设计不再有效.

但是importlib没有明确的解决方案.如何使用importlib实现导入后挂钩?

Gra*_*ton 6

wrapt模块提供了一个实现。

观看有关 的视频wrapt,其中包括此功能:

认为文档wrapt还没有提到它。

一些博客文章位于:

不过还是谈谈吧。

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


mgi*_*son 5

我会惊讶地发现这是做到这一点的最佳方式......但是,从早期的 python2.x 版本开始,__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')
Run Code Online (Sandbox Code Playgroud)

这个答案为注册回调提供了一个超级简单的注册表。装饰器只是注册函数然后返回它。它不做任何花哨的检查(例如,模块是否已经加载),但可以很容易地扩展到做到这一点。

明显的缺点是,如果其他模块决定猴子补丁__import__,那么你就不走运了——这个模块或另一个模块最终可能会坏掉。

我已经对此进行了测试,它似乎适用于 python2.x 和 python3.x。