Python缓存键装饰器中key变量触发UnboundLocalError的原因分析
装饰器缓存键生成的常见作用域错误
第一版代码触发NameError的原因
- 代码中在
wrapper内部使用global key的声明逻辑完全错误 cache_key_gen内部定义的key属于函数局部变量,并不在模块全局作用域下- 一旦加上
global声明,Python会直接跳过所有外层闭包作用域,去模块最顶层查找名为key的变量;而模块顶层从未定义过这个变量,因此触发变量未定义的NameError
第二版代码触发UnboundLocalError的原因
- 去掉
global声明后的报错,来自Python的静态作用域判定规则 - Python在编译函数时,只要检测到函数内部存在对某变量的赋值操作,就会直接将该变量标记为函数的局部变量,不会向上查找外层作用域的同名变量
- 代码中的
key += str(arg)本质是对key的重新赋值,因此Python会把key判定为wrapper的局部变量;实际运行时在给这个局部变量完成赋值前就尝试读取它,就会触发“局部变量未绑定”的UnboundLocalError
可行的修正方案
- 方案1:使用
nonlocal关键字明确作用域
在wrapper内部加上nonlocal key声明,明确告知Python这个变量引用自外层嵌套闭包,不要将其判定为wrapper的局部变量,修正后的核心代码结构:def cache_key_gen(func): key = func.__name__ def wrapper(*args): nonlocal key for arg in args: key += str(arg) print(key) return func(*args) return wrapper - 方案2:避免修改外层变量,每次调用时实时生成缓存键
不要在外层闭包维护可变的key状态,每次执行wrapper时,基于基础的函数名和入参动态拼接生成完整缓存键,从根源上避免作用域冲突,同时也能解决多次调用时key无限累加的逻辑问题:def cache_key_gen(func): base_key = func.__name__ def wrapper(*args): full_key = base_key + ''.join(str(arg) for arg in args) print(full_key) return func(*args) return wrapper
内容的提问来源于stack exchange,提问作者Agyey Arya
相关产品推荐
相关产品推荐

