2017-02-01 10 views
2

Так что я столкнулся с этой неприятной ошибкой сегодня в своем коде. Я только недавно использовал свойства Python и использовал его для сохранения старого значения свойства в другой переменной всякий раз, когда свойство было обновлено. Поэтому, когда вызывается функция setter, мне нужно сохранить старое значение, а затем новое значение для передачи.Это ошибка? Свойство Python работает некорректно с + =

Оказывается, при использовании += на свойстве вызывается метод __iadd__, обновляющий свойство на месте, затем вызывается сеттер. Но к тому времени старое значение теряется. Вот несколько примеров, иллюстрирующих проблему.

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, и что было пропущено. Так что мне интересно, если это ошибка, я что-то упустил, или просто не должен использовать + =, если я имею дело с свойством и нуждаюсь в старом значении.

+0

Вы можете проверить http://stackoverflow.com/questions/11987949/how-to-implement-iadd-for-a -python-property – Jan

+0

Это почти помогло. Если бы я просто заменил '__iadd__' на' __add__' на объектах, тогда он исправит его. Но только моя удача '__Iadd__' является атрибутом ready-only для массивов numpy. Поэтому я просто избавился от сеттера и сделал его функцией несобственного набора. – TheLoneMilkMan

ответ

0

Tricky. Вы можете кэшировать старое значение в получателе. Это немного раздражает, потому что операция кеширования выполняется при каждом вызове получателя, а не в вызовах сеттера. Но в любом случае ...

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