如何扩展函数的上下文?例如在装饰器中让被装饰函数访问闭包logger
装饰器场景下向被装饰函数注入闭包变量的实现方案
你尝试的exec方案不生效,是因为exec的执行上下文和被调用函数自身的执行上下文是隔离的,func执行时的变量查找不会走exec传入的命名空间。可通过以下三种常见方案实现需求:
方法1:关键字参数注入(最推荐,无副作用)
直接在调用被装饰函数时,将logger作为额外关键字参数传入,被装饰函数预留对应参数或者用**kwargs接收即可,线程安全无副作用。
from functools import wraps import logging def logged(func): # 闭包内初始化logger logger = logging.getLogger(func.__name__) logger.setLevel(logging.INFO) handler = logging.StreamHandler() logger.addHandler(handler) @wraps(func) def _logged(*args, **kwargs): # 额外传入logger参数 return func(*args, logger=logger, **kwargs) return _logged # 被装饰函数使用示例1:预留logger参数 @logged def test(a, b, logger=None): logger.info(f"计算结果为: {a + b}") return a + b # 被装饰函数使用示例2:用**kwargs接收,无需修改原有参数定义 @logged def test2(a, b, **kwargs): kwargs['logger'].info(f"计算结果为: {a + b}") return a + b test(1,2) test2(3,4)
方法2:函数属性临时绑定
利用Python函数是第一类对象、可自定义属性的特性,调用前把logger绑定到函数对象上,单线程场景可直接使用。
from functools import wraps import logging def logged(func): logger = logging.getLogger(func.__name__) logger.setLevel(logging.INFO) handler = logging.StreamHandler() logger.addHandler(handler) @wraps(func) def _logged(*args, **kwargs): # 给函数对象绑定logger属性 func.logger = logger res = func(*args,**kwargs) # 可选:调用完成后删除属性,避免外部误访问 # del func.logger return res return _logged # 被装饰函数使用示例 @logged def test(a, b): test.logger.info(f"计算结果为: {a + b}") return a + b test(1,2)
方法3:临时修改函数全局命名空间(仅特殊场景使用)
如果希望被装饰函数内部可以直接像访问全局变量一样使用logger,不需要加参数或前缀,可以临时修改函数的__globals__字典,调用完成后恢复原有值即可。注意该方案线程不安全,性能相对更低。
from functools import wraps import logging def logged(func): logger = logging.getLogger(func.__name__) logger.setLevel(logging.INFO) handler = logging.StreamHandler() logger.addHandler(handler) @wraps(func) def _logged(*args, **kwargs): # 备份原有全局空间中logger的值(如果存在的话) original_logger = func.__globals__.get('logger') # 注入logger到全局空间 func.__globals__['logger'] = logger try: res = func(*args,**kwargs) finally: # 恢复原有值,避免污染全局命名空间 if original_logger is not None: func.__globals__['logger'] = original_logger else: del func.__globals__['logger'] return res return _logged # 被装饰函数使用示例 @logged def test(a, b): # 直接访问logger变量即可 logger.info(f"计算结果为: {a + b}") return a + b test(1,2)
内容的提问来源于stack exchange,提问作者просто_прогер
相关产品推荐
相关产品推荐

