复合赋值的属性设置器?

Posted

技术标签:

【中文标题】复合赋值的属性设置器?【英文标题】:Property setters for compound assignment? 【发布时间】:2011-10-12 01:56:15 【问题描述】:

考虑以下简单的示例类,它有一个属性,在调用时会公开一些内部数据的修改版本:

类Foo(对象):
    def __init__(self, value, offset=0):
        self._value = 价值
        self.offset = 偏移量

    @财产
    默认值(自我):
        返回 self._value + self.offset

    @value.setter
    定义值(自我,价值):
        self._value = 价值

value.setter 适用于常规分配,但对于复合分配当然会失效:

>>> x = Foo(3, 2)
>>> x.值
5
>>> x.value = 2
>>> x.值
4
>>> x.值 += 5
>>> x.值
11

期望的行为是x.value += 5 应该等效于x.value = x._value + 5,而不是x.value = x.value + 5。有什么方法可以纯粹在Foo 类中实现这一点(使用属性接口)?

【问题讨论】:

我建议不要从一开始就将神奇的数字行为添加到属性中,否则你会破坏对称性——正如你所注意到的——使用标准的数学运算符。定义offsetValue() 使读者的意图更加明显;特别是,x.value += 5 是否应该添加到 _valueoffset 我用if 'INPLACE_ADD' in [dis.opname[ord(op)] for op in inspect.stack()[1][0].f_code.co_code]: 尝试了一些东西,然后才意识到setter 的行为总体上是有缺陷的。我已经使用它这么久了,我没想到只是重新审视它。 如果没有这个偏移量,它似乎可以正常工作。我同意 ezod 的观点,即尝试将 add 的结果分配给计算属性时它正在做一些奇怪的事情。使用偏移量,它会产生意想不到的结果。没有它就没有。嗯。 【参考方案1】:

@ezod,即使使用描述符协议,也没有直接的方法可以满足您的要求。 value 属性的这种行为完全破坏了 += 运算符的语义。

【讨论】:

我不确定我是否理解。它是如何破坏+= 语义的?在外部,它将完全按预期工作。 “偏移”功能(在此示例中很简单,但在我的实际应用程序中涉及)对用户隐藏,但用户仍然应该能够使用其类的所有功能直观地访问和修改公开的值(包括__iadd__) 没有这些奇怪的效果。 经过进一步思考,我想我明白你的意思了——x += y 应该等同于x = x + y,在我的情况下这不是真的。很公平。 考虑这个:y = x.value; x.value += 5; y += 5;此时,程序员期望 x == y。【参考方案2】:

覆盖__iadd__ 魔术方法。

您需要这样做,因为+= 意味着“取值并添加五,然后将其设置为结果”。如果你想让它知道值不是真正的值,你需要改变+= 操作符的语义。

【讨论】:

Foo 中覆盖__iadd__ 将不起作用,因为我没有添加到Foo,我正在添加到它的成员。即使我可以访问value 的类并且没有多态性,它也不知道Foo 内部,所以我也不能在那里覆盖__iadd__ __iadd__ 也只在可变类型上被调用,否则__add__ 被调用。此外,尝试通过子类化传递的值来编写其工作的语义——无论你做什么,你最终都会递归。【参考方案3】:

我遇到了同样的问题,用下面的方法解决了:

class Foo(object):
    def __init__(self):
        self._y = 0

    @property
    def y(self):
        return self._y + 5

    @y.setter
    def y(self, value):
        value -= 5
        self._y = value


>>> f = Foo()
>>> f.y
5
>>> f.y += 1
>>> f._y
1
>>> f.y
6

这可能是一个解决方案吗?我是否忽略了什么?

【讨论】:

【参考方案4】:

期望的行为是 x.value += 5 应该等价于 x.value = x._value + 5

为什么 Python 应该知道您的属性是如何实现的(即 setter 将其值完全分配给 _x)?

property 的协议使您可以将不同的操作(获取、设置、删除)分配给一个名称。而且这个协议并不关心这些动作的实现方式。

因此,如果 Python 对您的代码做出一些假设并尝试修改严格的代码逻辑,那将是相当混乱的。

【讨论】:

我并不是说 Python 应该知道这一点。更多类似setter 能够自省它是如何被调用的(即通过=+= 等)并做出相应的行为。

以上是关于复合赋值的属性设置器?的主要内容,如果未能解决你的问题,请参考以下文章

CSS3样式,基础选择器,复合选择器

web前端练习31----Css,选择器(基本选择器,复合选择器,属性选择器,伪类,伪元素,优先级,雪碧图练习)

web前端练习31----Css,选择器(基本选择器,复合选择器,属性选择器,伪类,伪元素,优先级,雪碧图练习)

无法设置对象 - 只读属性或未找到设置器

对象的理解和解构赋值

ObjC 属性和 C 运算符