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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 02:15:03