Python attrs转换器中使用lambda函数出现异常行为的问题求助
Lambda转换器在attrs字段转换器中的闭包陷阱问题
在使用attrs库的field_transformer自动创建依赖元数据的转换器时,用lambda函数作为转换器会出现异常行为:元数据未被正确识别,调换字段顺序后所有字段共用最后一个字段的元数据值。
代码示例
import attrs def add(x, y=10): return x + y def func(cls, fields): results = [] for f in fields: y = f.metadata.get('add', None) converter = lambda x: add(x, y) if (y is not None) else add(x) print('Name:', f.name, y, converter(0)) results.append(f.evolve(converter=converter)) return results
异常现象
- 字段a带元数据、b不带的情况
Data1 = attrs.make_class('Data1', {'a': attrs.field(default=1, metadata={'add': 5}), 'b': attrs.field(default=1)}, field_transformer=func) d1= Data1() d1
输出:
Name: a 5 5 Name: b None 10 实例结果:Data1(a=11, b=11)
本该a用y=5得到6,b用默认y=10得到11,但实际两者都是11。
- 调换字段顺序后
Data2 = attrs.make_class('Data2', {'b': attrs.field(default=1), 'a': attrs.field(default=1, metadata={'add': 5})}, field_transformer=func) d2 = Data2() d2
输出:
Name: b None 10 Name: a 5 5 实例结果:Data2(b=6, a=6)
本该b用默认y=10得到11,a用y=5得到6,但实际两者都是6。
原因分析
这是Python闭包的延迟绑定特性导致的:lambda函数中的y变量并不是在定义时就捕获当前循环的y值,而是在lambda被调用(即attrs处理字段时)才会去查找y的当前值。循环结束后,y会保留最后一次循环的赋值结果,所以所有lambda都会共用这个最终的y值:
- Data1循环最后
y是None,所以两个转换器都用y=None,调用add(x,10)得到11; - Data2循环最后
y是5,所以两个转换器都用y=5,调用add(x,5)得到6。
解决方案
通过给lambda添加默认参数,在定义时就捕获当前循环的y值,避免延迟绑定的问题:
def func(cls, fields): results = [] for f in fields: y = f.metadata.get('add', None) # 用默认参数y=y捕获当前循环的y值 converter = lambda x, y=y: add(x, y) if (y is not None) else add(x) print('Name:', f.name, y, converter(0)) results.append(f.evolve(converter=converter)) return results
修改后再运行两个Data类,就能得到预期结果:
- Data1实例:
Data1(a=6, b=11) - Data2实例:
Data2(b=11, a=6)
内容的提问来源于stack exchange,提问作者slaughter98
相关产品推荐
相关产品推荐

