【问题标题】:attributes that modify other attributes修改其他属性的属性
【发布时间】:2021-11-25 08:07:10
【问题描述】:

我想创建一个具有以下行为的Block 类:如果设置了 iip3,则 oip3 设置为 iip3 + 增益。如果设置了 oip3,则将 iip3 设置为 oip3 - 增益如下:

b = Block(gain = 10)
Block(gain=10, iip3=inf, oip3=inf)

b.oip3 = 30
Block(gain=10, iip3=20, oip3=30)

b.iip3 = 21
Block(gain=10, iip3=21, oip3=31)

我已经用 attrs 模块尝试过这个:

import attr
import numpy as np

def set_oip3(instance, attribute, iip3):
    instance.oip3 = iip3 + instance.gain
    return iip3

def set_iip3(instance, attribute, oip3):
    instance.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)

但是,这不起作用。我怀疑对set_oip3 的调用会导致调用set iip3,然后调用set oip3。

必须有使用 attrs 的解决方案。

【问题讨论】:

  • 您是否有任何理由专门寻找使用attrs 的解决方案?
  • 如果在运行期间调整增益,所有值都会重置吗?您使用的那些值是实例属性,还是类属性?
  • 之所以使用attrs,是因为它减少了很多样板代码。但是,鉴于以下解决方案,我可能会重新考虑。
  • 至于如果增益发生变化会发生什么,这是一个很好的问题,我需要选择一种有意义的行为。如果增益发生变化,则 oip3 可能会保持固定,并且 iip3 会重新计算。
  • 如果任一答案令人满意,请继续接受一个。如果没有,请更新您的要求!

标签: python python-attrs


【解决方案1】:

你可以使用@property装饰器:

import numpy as np


class Block:
    def __init__(self, gain=10, iip3=np.inf, oip3=np.inf):
        self.gain = gain
        self._iip3 = iip3
        self._oip3 = oip3

    def __repr__(self):
        return f"Block(gain={self.gain}, iip3={self.iip3}, oip3={self.oip3})"

    @property
    def iip3(self):
        return self._iip3

    @iip3.setter
    def iip3(self, val):
        self._iip3 = val
        self._oip3 = self._iip3 + self.gain

    @property
    def oip3(self):
        return self._oip3

    @oip3.setter
    def oip3(self, val):
        self._oip3 = val
        self._iip3 = self._oip3 - self.gain

用法:

In [2]: b = Block()

In [3]: b.iip3 = 21

In [4]: b
Out[4]: Block(gain=10, iip3=21, oip3=31)

In [5]: b.oip3 = 30

In [6]: b
Out[6]: Block(gain=10, iip3=20, oip3=30)

对于它的价值来说,让 mutator 对除了给定 mutator 应该修改的属性之外的属性产生副作用并不是一个好主意。

【讨论】:

    【解决方案2】:

    正如其他人所说,在属性更改时改变属性并不是一个好主意。但如果你坚持,你可以通过使用object.__setattr__ 来规避on_setattr 钩子——这也是attrs 在内部所做的事情:

    import attr
    import numpy as np
    
    def set_oip3(instance, attribute, iip3):
        object.__setattr__(instance, "oip3", iip3 + instance.gain)
        return iip3
    
    def set_iip3(instance, attribute, oip3):
        object.__setattr__(instance, "iip3", oip3 - instance.gain)
        return oip3
    
    @attr.define
    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)
    

    按您的要求工作:

    In [9]: b = Block(gain = 10)
    
    In [10]: b
    Out[10]: Block(gain=10, iip3=inf, oip3=inf)
    
    In [11]: b.oip3 = 30
    
    In [12]: b
    Out[12]: Block(gain=10, iip3=20, oip3=30)
    
    In [13]: b.iip3 = 21
    
    In [14]: b
    Out[14]: Block(gain=10, iip3=21, oip3=31)
    

    【讨论】:

    • 两种解决方案都很有用,但这个解决方案专门针对原始问题使用attrs
    猜你喜欢
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多