如何使用attrs库实现iip3与oip3联动更新的Block类
解决方法
你遇到的确实是两个属性的on_setattr钩子互相触发导致的无限递归问题,只要绕过attrs的属性设置钩子直接修改底层存储,就能避免循环。
正确实现代码
import attr import numpy as np def set_oip3(instance, attribute, iip3): # 直接修改__dict__不会触发oip3的on_setattr钩子,避免循环 instance.__dict__['oip3'] = iip3 + instance.gain return iip3 def set_iip3(instance, attribute, oip3): instance.__dict__['iip3'] = oip3 - instance.gain return oip3 @attr.s class Block: gain = attr.ib() iip3 = attr.ib(on_setattr=set_oip3, default=np.inf) oip3 = attr.ib(on_setattr=set_iip3, default=np.inf) def __attrs_post_init__(self): # 处理初始化阶段的自动赋值,若用户同时传iip3和oip3默认以iip3为准,可自行调整优先级 if self.iip3 is not np.inf: self.__dict__['oip3'] = self.iip3 + self.gain elif self.oip3 is not np.inf: self.__dict__['iip3'] = self.oip3 - self.gain
效果测试
b = Block(gain = 10) print(b) # Block(gain=10, iip3=inf, oip3=inf) b.oip3 = 30 print(b) # Block(gain=10, iip3=20, oip3=30) b.iip3 = 21 print(b) # Block(gain=10, iip3=21, oip3=31)
实现说明
attrs的属性设置钩子只有在通过.运算符直接赋值属性时才会触发,直接操作实例的__dict__字典是修改属性的底层存储,不会触发任何钩子逻辑,自然就不会出现两个钩子互相调用的死循环问题。加__attrs_post_init__是因为初始化阶段attrs不会触发on_setattr钩子,所以需要手动处理初始化时用户传入了iip3或者oip3的场景。
内容的提问来源于stack exchange,提问作者justin
相关产品推荐
相关产品推荐

