Skip to content

🥕 Przeciążanie operatorów

Technika, która pozwala definiować, jak klasy będą odpowiadać na standardowe operacje, takie jak np. dodawanie, porównywanie czy wyświetlanie reprezentacji tekstowej.

W Pythonie przeciążanie odbywa się przez definiowanie w klasie specjalnych metod (tzw. dunder methods - od double underscore methods), które zaczynają i kończą podwójnym podkreśleniem (np. __init__, __add__ czy __str__).

class Wektor:
    # Ta metoda odpowiada za inicjalizację
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # Metoda przeciążająca operator +
    # Definiuje dodawanie wiektora do wektora
    def __add__(self, inny_wektor):
        return Wektor(self.x + inny_wektor.x, self.y + inny_wektor.y)

    # Metoda przeciążająca operator <
    def __lt__(self, inny_wektor):
        return (self.x**2 + self.y**2) < (inny_wektor.x**2 + inny_wektor.y**2)

    # Definiuje tekstową reprezentację obiektu
    def __str__(self):
        return f"Wektor({self.x}, {self.y})"

wektor1 = Wektor(2, 3)
wektor2 = Wektor(1, 1)
suma = wektor1 + wektor2
print(suma)
print(wektor1 < wektor2)

__str__ vs __repr__

Obie metody definiują tekstową reprezentację obiektu, ale służą różnym celom:

Metoda Wywoływana przez Przeznaczenie
__str__ print(), str() Czytelna dla człowieka - dla użytkownika końcowego
__repr__ repr(), konsola interaktywna, debugger Techniczna - dla programisty, powinna umożliwiać odtworzenie obiektu

Jeśli zdefiniujesz tylko __repr__, Python użyje jej również zamiast __str__.

class Punkt:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"Punkt({self.x}, {self.y})"

    def __repr__(self):
        return f"Punkt(x={self.x!r}, y={self.y!r})"

p = Punkt(1, 2)
print(p)        # Punkt(1, 2)        - używa __str__
print(repr(p))  # Punkt(x=1, y=2)   - używa __repr__

Tip

Zasada kciuka: __repr__ powinna wyglądać jak wyrażenie, którym można odtworzyć obiekt. __str__ może być bardziej opisowa.

Rozszerzona lista metod do przeciążania operatorów
Metoda Przeciąża Wywoływana dla
__init__ Konstruktor Tworzenie obiektu - x = Klasa(args)
__del__ Destructor Zwolnienie obiektu x
__add__ Operator + x + y, x += y, jeśli nie ma __iadd__
__or__ Operator ` ` (OR poziomu bitowego)
__repr__, __str__ Wyświetlanie, konwersje print(x), repr(x), str(x)
__call__ Wywołanie funkcji x(*args, **kargs)
__getattr__ Odczytanie atrybutu x.niezdefiniowany_atrybuty
__setattr__ Przypisanie atrybutu x.atrybut = wartosc
__delattr__ Usuwanie atrybutu del x.atrybut
__getattribute__ Przechwytywanie atrybutu x.atrybut
__getitem__ Indeksowanie, wycinanie, iteracje x[klucz], x[i:j], pętle for oraz inne iteracje, jeśli nie ma __iter__
__setitem__ Przypisanie indeksu i wycinka x[klucz] = wartosc, x[i:j] = sekwencja
__delitem__ Usuwanie indeksu i wycinka del x[klucz], del x[i:j]
__len__ Długość len(x), testy prawdziwości, jeśli nie ma __bool__
__bool__ Testy logiczne bool(x), testy prawdziwości
__lt__, __gt__, __le__, __ge__, __eq__, __ne__ Porównania x < y, x > y, x <= y, x >= y, x == y, x != y
__radd__ Prawostronny operator + nieinstancja + x
__iadd__ Dodawanie w miejscu (rozszerzone) x += y
__iter__, __next__ Konteksty iteracyjne i = iter(x), next(i); pętle for, jeśli nie ma __contains__, testy in, wszystkie listy składanie, funkcje map(f,x)
__contains__ Test przynależności item in x (dowolny iterator)
__index__ Wartość całkowita hex(x), bin(x), oct(x), o[x], o[x:]
__enter__, __exit__ Menedżer konktekstu with obj as var:
__get__, __set__, __delete__ Atrybuty deskryptorów x.attr, x.attr = value, del x.attr
__new__ Tworzenie instancji Tworzenie instancji, przed __init__