10.12 导入模�的�时修改模�¶
问题¶
ä½ æƒ³ç»™æŸ�个已å˜åœ¨æ¨¡å�—ä¸çš„å‡½æ•°æ·»åŠ è£…é¥°å™¨ã€‚ ä¸�过,å‰�æ��是这个模å�—å·²ç»�被导入并且被使用过。
解决方案¶
è¿™é‡Œé—®é¢˜çš„æœ¬è´¨å°±æ˜¯ä½ æƒ³åœ¨æ¨¡å�—è¢«åŠ è½½æ—¶æ‰§è¡ŒæŸ�个动作。 å�¯èƒ½æ˜¯ä½ 想在一个模å�—è¢«åŠ è½½æ—¶è§¦å�‘æŸ�个回调函数æ�¥é€šçŸ¥ä½ 。
这个问题å�¯ä»¥ä½¿ç”¨10.11å°�节ä¸å�Œæ ·çš„导入钩å�机制æ�¥å®žçŽ°ã€‚ä¸‹é�¢æ˜¯ä¸€ä¸ªå�¯èƒ½çš„æ–¹æ¡ˆï¼š
# postimport.py
import importlib
import sys
from collections import defaultdict
_post_import_hooks = defaultdict(list)
class PostImportFinder:
def __init__(self):
self._skip = set()
def find_module(self, fullname, path=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 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
def when_imported(fullname):
def decorate(func):
if fullname in sys.modules:
func(sys.modules[fullname])
else:
_post_import_hooks[fullname].append(func)
return func
return decorate
sys.meta_path.insert(0, PostImportFinder())
è¿™æ ·ï¼Œä½ å°±å�¯ä»¥ä½¿ç”¨ when_imported() 装饰器了,例如:
>>> from postimport import when_imported
>>> @when_imported('threading')
... def warn_threads(mod):
... print('Threads? Are you crazy?')
...
>>>
>>> import threading
Threads? Are you crazy?
>>>
作为一个更实际的例å�ï¼Œä½ å�¯èƒ½æƒ³åœ¨å·²å˜åœ¨çš„定义上é�¢æ·»åŠ è£…é¥°å™¨ï¼Œå¦‚ä¸‹æ‰€ç¤ºï¼š
from functools import wraps
from postimport import when_imported
def logged(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Calling', func.__name__, args, kwargs)
return func(*args, **kwargs)
return wrapper
# Example
@when_imported('math')
def add_logging(mod):
mod.cos = logged(mod.cos)
mod.sin = logged(mod.sin)
讨论¶
本节技术ä¾�赖于10.11å°�节ä¸è®²è¿°è¿‡çš„导入钩å�,并ç¨�作修改。
@when_imported 装饰器的作用是注册在导入时被激活的处�器函数。
该装饰器检查sys.modulesæ�¥æŸ¥çœ‹æ¨¡å�—是å�¦çœŸçš„å·²ç»�è¢«åŠ è½½äº†ã€‚
如果是的è¯�,该处ç�†å™¨è¢«ç«‹å�³è°ƒç”¨ã€‚ä¸�然,处ç�†å™¨è¢«æ·»åŠ åˆ° _post_import_hooks å—å…¸ä¸çš„一个列表ä¸åŽ»ã€‚
_post_import_hooks 的作用就是收集所有的为�个模�注册的处�器对象。
一个模��以注册多个处�器。
è¦�让模å�—导入å�Žè§¦å�‘æ·»åŠ çš„åŠ¨ä½œï¼ŒPostImportFinder 类被设置为sys.meta_pathç¬¬ä¸€ä¸ªå…ƒç´ ã€‚
它会�获所有模�导入�作。
本节ä¸çš„ PostImportFinder 的作用并ä¸�æ˜¯åŠ è½½æ¨¡å�—,而是自带导入完æˆ�å�Žè§¦å�‘相应的动作。
实际的导入被委派给ä½�于sys.meta_pathä¸çš„其他查找器。
PostImportLoader ç±»ä¸çš„ imp.import_module() 函数被递归的调用。
为了é�¿å…�é™·å…¥æ— çº¿å¾ªçŽ¯ï¼ŒPostImportFinder ä¿�æŒ�äº†ä¸€ä¸ªæ‰€æœ‰è¢«åŠ è½½è¿‡çš„æ¨¡å�—集å�ˆã€‚
如果一个模å�—å��å˜åœ¨å°±ä¼šç›´æŽ¥è¢«å¿½ç•¥æŽ‰ã€‚
当一个模å�—被 imp.import_module() åŠ è½½å�Žï¼Œ
所有在_post_import_hooks被注册的处ç�†å™¨è¢«è°ƒç”¨ï¼Œä½¿ç”¨æ–°åŠ è½½æ¨¡å�—作为一个å�‚数。
有一点需è¦�注æ„�的是本机ä¸�适用于那些通过 imp.reload() 被显å¼�åŠ è½½çš„æ¨¡å�—。
ä¹Ÿå°±æ˜¯è¯´ï¼Œå¦‚æžœä½ åŠ è½½ä¸€ä¸ªä¹‹å‰�å·²è¢«åŠ è½½è¿‡çš„æ¨¡å�—,那么导入处ç�†å™¨å°†ä¸�会å†�被触å�‘。
å�¦å¤–,è¦�æ˜¯ä½ ä»Žsys.modulesä¸åˆ 除模å�—ç„¶å�Žå†�é‡�新导入,处ç�†å™¨å�ˆä¼šå†�一次触å�‘。
更多关于导入å�Žé’©å�ä¿¡æ�¯è¯·å�‚考 PEP 369.