2017-02-01 3 views
2

So stieß ich heute in meinem Code auf diesen bösen Bug. Ich habe gerade Python-Eigenschaften verwendet und verwendet, um den alten Wert der Eigenschaft in einer anderen Variablen beizubehalten, wenn die Eigenschaft aktualisiert wurde. Also, wenn die Setter-Funktion aufgerufen wird, brauchte ich den alten Wert zu speichern, und dann den neuen Wert zu übertragen.Ist das ein Fehler? Python-Eigenschaft funktioniert nicht richtig mit + =

Es stellt sich heraus, dass bei Verwendung += für eine Eigenschaft die __iadd__ Methode aufgerufen wird, die Eigenschaft vor Ort zu aktualisieren, dann wird der Setter aufgerufen. Aber zu dieser Zeit ist der alte Wert verloren. Hier ist ein Code, der das Problem veranschaulicht.

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) 

Bei der Ausführung gibt es dieses Ergebnis:

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 

Es sagt, dass es 4 bis 4 zu ändern, aber es ist wirklich 3 bis 4 verändert und dass verpasst wurde. Ich frage mich also, ob das ein Fehler ist, mir etwas fehlt oder ich einfach nicht + = verwenden soll, wenn ich mit einer Eigenschaft zu tun habe und den alten Wert brauche.

+0

Vielleicht möchten Sie auschecken http://stackoverflow.com/questions/11987949/how-to-implement-iadd-for-a -python-property – Jan

+0

Das hat fast geholfen. Wenn ich gerade "__iadd__" durch "__add__" auf den Objekten ersetzt hätte, würde es das beheben. Aber nur mein Glück '__Iadd__' ist ein Ready-Only-Attribut für numpige Arrays. Also habe ich den Setter einfach losgeworden und ihn zu einer Nicht-Eigentums-Set-Funktion gemacht. – TheLoneMilkMan

Antwort

0

Tricky. Sie könnten den alten Wert im Getter zwischenspeichern. Das ist ein bisschen nervig, da die Zwischenspeicherung bei jedem Aufruf des Getters statt bei den Setter-Aufrufen durchgeführt wird. Aber wie auch immer ...

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) 

Ausgang

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