2009-06-26 23 views
38

Czy można zastąpić + = w Pythonie?Przesłanianie "+ =" w Pythonie? (__iadd __() metoda)

+1

Dokładny duplikat http://stackoverflow.com/questions/728361/is-there-a-way-to-overload-in-python –

+4

Tak, ale za pomocą wyszukiwania w witrynie: + = python nie daje wyników. umieszczanie cudzysłowów wokół "+ =" daje wyniki, ale w większości nieistotne sędziowanie przez nagłówki. znaleźć to za pomocą wyszukiwarki Google. –

Odpowiedz

76

Tak, należy zastąpić metodę __iadd__. Przykład:

def __iadd__(self, other): 
    self.number += other.number 
    return self  
+30

Nie powinieneś zaimplementować '__iadd__', jeśli twoja klasa reprezentuje niezmienne obiekty. W takim przypadku wystarczy zaimplementować '__add__', który będzie zastępował' + = '. Na przykład możesz użyć '+ =' na niezmiennych typach, takich jak łańcuchy i liczby całkowite, których nie można wykonać za pomocą '__iadd__'. –

+0

@ScottGriffiths, więc czy mówisz, że jeśli zaimplementowałeś '__add__', to niekoniecznie musisz implementować' __iadd__'? Przeczytałem zduplikowane pytanie, które podłączyłeś, ale właśnie się pogubiłem, ponieważ nie rozumiem, dlaczego chcesz zaimplementować '__add__', aby zmutował obiekt –

+0

@ScottGriffiths oznaczało pytanie" nie musisz koniecznie implementować '__iadd__' __do użycia + = __? " –

12

Poza przeciążeniem __iadd__ (pamiętaj, aby wrócić siebie!), Można również awaryjne na __add__, a x + y = zadziała jak x = x + y. (Jest to jedna z pułapek operatora + =.)

>>> class A(object): 
... def __init__(self, x): 
...  self.x = x 
... def __add__(self, other): 
...  return A(self.x + other.x) 
>>> a = A(42) 
>>> b = A(3) 
>>> print a.x, b.x 
42 3 
>>> old_id = id(a) 
>>> a += b 
>>> print a.x 
45 
>>> print old_id == id(a) 
False 

nawet trips up experts:

class Resource(object): 
    class_counter = 0 
    def __init__(self): 
    self.id = self.class_counter 
    self.class_counter += 1 

x = Resource() 
y = Resource() 

Jakie wartości można się spodziewać x.id, y.id i Resource.class_counter mieć?

+7

Twój drugi przykład nie ma nic wspólnego z iadd ani + =. Ten sam wynik występuje, jeśli użyjesz self.class_counter = self.class_counter + 1 To tylko problem z określeniem zakresu, używając self, kiedy należy użyć Resource. – FogleBird

+0

Jest to przykład użycia + = może prowadzić do problemów. Jeśli przeładowujesz __iadd__, to otwierasz użytkowników swojej klasy (włączając w to siebie) do tego i, przynajmniej, powinieneś wiedzieć, że problem istnieje wcześniej. –

+2

@FogleBird: To jest błąd, ponieważ 'foo + = bar' może oznaczać" zmutować istniejący obiekt, który 'foo' oznacza" lub "przypisać' foo' do obiektu wynikającego z wyrażenia 'foo + bar'". A to, co się dzieje, zależy od tego, czy 'foo' ma metodę' __iadd__'. – Claudiu

9

Oprócz co prawidłowo podano w odpowiedzi powyżej, warto wyraźnie wyjaśnić, że kiedy __iadd__ jest nadpisane, operacja x += y nie kończy się wraz z końcem __iadd__ metody.

Zamiast tego kończy się x = x.__iadd__(y). Innymi słowy, Python przypisuje wartość zwracaną przez implementację do obiektu, do którego "dodajesz", PO zakończeniu implementacji.

Oznacza to, że możliwe jest zmutowanie lewej strony operacji x += y, aby nie zakończył się ostatni niejawny krok. Zastanów się, co może się zdarzyć podczas dodawania do czegoś, co jest w liście:

>>> x[1] += y # x has two items

Teraz, jeśli realizacja __iadd__ (metoda obiektu w x[1]) omyłkowo lub celowo usuwa pierwszy element (x[0]) od początku listy, Python uruchomi następnie twoją metodę __iadd__) spróbuj przypisać jej wartość zwracaną do x[1]. Który już nie będzie istnieć (będzie to x[0]), w wyniku czego powstanie ÌndexError.

Lub, jeśli __iadd__ wkładki coś do początku x powyższego przykładu, Twój obiekt będzie na x[2], nie x[1], a co było wcześniej w x[0] będzie teraz w x[1] i przypisać wartość zwracaną przez __iadd__ wezwanie.

O ile nie wiadomo, co się dzieje, powstałe błędy mogą być koszmarem do naprawienia.