You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何扩展函数的上下文?例如在装饰器中让被装饰函数访问闭包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,提问作者просто_прогер

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 17:48:03