这是一个错误吗? Python属性与+ =无法正常工作

时间:2017-02-01 06:47:03

标签: python properties

所以我今天在我的代码中遇到了这个讨厌的bug。我刚刚使用Python属性,并在更新属性时使用它来保留另一个变量中属性的旧值。因此,当调用setter函数时,我需要保存旧值,然后传输新值。

事实证明,在属性上使用+=时,会调用__iadd__方法,就地更新属性,然后调用setter。但那时旧的价值已经消失了。这是一些说明问题的代码。

class Container(object):
    def __init__(self, val):
        self.val = val

    def __add__(self, other):
        print('in __add__', other)
        return Container(self.val + other.val)

    def __iadd__(self, other):
        print('in __iadd__:', other)
        self.val += other.val
        return self

    def __str__(self):
        return str(self.val)


class Test:
    def __init__(self):
        self._val = Container(0)

    @property
    def val(self):
        return self._val

    @val.setter
    def val(self, values):
        print("changing from {} to {}".format(self._val, values))
        self._val = values

test = Test()
print('val=', test.val)
test.val = Container(2)
print('val=', test.val)
test.val = test.val + Container(1)
print('val=', test.val)
test.val += Container(1)
print('val=', test.val)
test.val.__iadd__(Container(1))
print('val=', test.val)

运行时会得到以下结果:

val= 0
changing from 0 to 2
val= 2
in __add__ 1
changing from 2 to 3
val= 3
in __iadd__: 1
changing from 4 to 4
val= 4
in __iadd__: 1
val= 5

它表示它正在改变4到4,但它实际上正在改变3到4而且错过了。所以我想知道这是不是一个错误,我错过了一些东西,或者我不应该使用+ =如果我正在处理一个属性而需要旧值。

1 个答案:

答案 0 :(得分:0)

棘手。您可以在getter中缓存旧值。这有点令人讨厌,因为缓存操作是在每次调用getter时执行的,而不是在setter调用上执行的。但无论如何......

class Container(object):
    def __init__(self, val):
        self.val = val

    def __add__(self, other):
        print('in __add__', self.val, other)
        return Container(self.val + other.val)

    def __iadd__(self, other):
        print('in __iadd__:', self.val, other)
        self.val += other.val
        return self

    def __str__(self):
        return str(self.val)

class Test:
    def __init__(self):
        self._val = Container(0)
        self._prev_val = self._val.val

    @property
    def val(self):
        self._prev_val = self._val.val
        return self._val

    @val.setter
    def val(self, values):
        print("changing from {} to {}".format(self._prev_val, values))
        self._val = values

test = Test()
print('val=', test.val)
test.val = Container(2)
print('val=', test.val)
test.val = test.val + Container(1)
print('val=', test.val)
test.val += Container(1)
print('val=', test.val)
test.val.__iadd__(Container(1))
print('val=', test.val)

<强>输出

val= 0
changing from 0 to 2
val= 2
in __add__ 2 1
changing from 2 to 3
val= 3
in __iadd__: 3 1
changing from 3 to 4
val= 4
in __iadd__: 4 1
val= 5