================================ 10.12 导入模å—çš„åŒæ—¶ä¿®æ”¹æ¨¡å— ================================ ---------- 问题 ---------- ä½ æƒ³ç»™æŸä¸ªå·²å˜åœ¨æ¨¡å—ä¸çš„å‡½æ•°æ·»åŠ è£…é¥°å™¨ã€‚ ä¸è¿‡ï¼Œå‰ææ˜¯è¿™ä¸ªæ¨¡å—å·²ç»è¢«å¯¼å…¥å¹¶ä¸”被使用过。 ---------- 解决方案 ---------- è¿™é‡Œé—®é¢˜çš„æœ¬è´¨å°±æ˜¯ä½ æƒ³åœ¨æ¨¡å—è¢«åŠ è½½æ—¶æ‰§è¡ŒæŸä¸ªåŠ¨ä½œã€‚ å¯èƒ½æ˜¯ä½ 想在一个模å—è¢«åŠ è½½æ—¶è§¦å‘æŸä¸ªå›žè°ƒå‡½æ•°æ¥é€šçŸ¥ä½ 。 这个问题å¯ä»¥ä½¿ç”¨10.11å°èŠ‚ä¸åŒæ ·çš„å¯¼å…¥é’©åæœºåˆ¶æ¥å®žçŽ°ã€‚ä¸‹é¢æ˜¯ä¸€ä¸ªå¯èƒ½çš„æ–¹æ¡ˆï¼š .. code-block:: python # 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()`` 装饰器了,例如: .. code-block:: python >>> from postimport import when_imported >>> @when_imported('threading') ... def warn_threads(mod): ... print('Threads? Are you crazy?') ... >>> >>> import threading Threads? Are you crazy? >>> 作为一个更实际的例åï¼Œä½ å¯èƒ½æƒ³åœ¨å·²å˜åœ¨çš„å®šä¹‰ä¸Šé¢æ·»åŠ è£…é¥°å™¨ï¼Œå¦‚ä¸‹æ‰€ç¤ºï¼š .. code-block:: python 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 <https://www.python.org/dev/peps/pep-0369>`_.