CodeGym /Kursy /Python SELF PL /Przeciążanie operatorów

Przeciążanie operatorów

Python SELF PL
Poziom 20 , Lekcja 0
Dostępny

6.1 Metody magiczne

Przeciążanie operatorów w Pythonie pozwala określić lub zmienić zachowanie wbudowanych operatorów (na przykład +, -, *, /) dla klas użytkownika. Robi się to za pomocą specjalnych metod, które nazywają się metodami magicznymi.

Na przykład, w swojej klasie możesz przeciążyć operatory porównania:

Operator Metoda bez podkreślenia Sygnatura metody
== eq() __eq__(self, other)
!= ne() __ne__(self, other)
< lt() __lt__(self, other)
<= le() __le__(self, other)
> gt() __gt__(self, other)
>= ge() __ge__(self, other)

Załóżmy, że napisałeś swoją klasę i chcesz, aby obiekty Twojej klasy były porównywane dokładnie tak, jak tego potrzebujesz. Musisz po prostu zaimplementować w swojej klasie metodę «__eq__», i Python będzie ją wywoływał za każdym razem, gdy w kodzie porównywane są obiekty Twojej klasy.

Przykład:


class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
        
# Użycie
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2)  # Wyświetli: True
print(v1 == v3)  # Wyświetli: False

Za każdym razem, kiedy porównujesz dwa swoje obiekty, Python sprawdza, czy mają zaimplementowaną funkcję «__eq__». Jeśli tak, to ją wywołuje. Jeśli nie ma, to porównuje tylko referencje do obiektów.

Faktycznie w przykładzie powyżej jest napisane (tylko trzeba jeszcze dodać sprawdzenie, czy metoda istnieje):


# Użycie
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2))  # Wyświetli: True
print(v1.__eq__(v3))  # Wyświetli: False

6.2 Lista wszystkich operatorów

W sumie do przeciążenia dostępnych jest 6 grup operatorów.

Operatory arytmetyczne:

Operator Metoda bez podkreślenia Sygnatura metody
+ add __add__(self, other)
- sub __sub__(self, other)
* mul __mul__(self, other)
/ truediv __truediv__(self, other)
// floordiv __floordiv__(self, other)
% mod __mod__(self, other)
** pow __pow__(self, other)

Operatory porównania:

Operator Metoda bez podkreślenia Sygnatura metody
== eq __eq__(self, other)
!= ne __ne__(self, other)
< lt __lt__(self, other)
<= le __le__(self, other)
> gt __gt__(self, other)
>= ge __ge__(self, other)

Operatory logiczne:

Operator Metoda bez podkreślenia Sygnatura metody
& and __and__(self, other)
| or __or__(self, other)
^ xor __xor__(self, other)
~ invert __invert__(self)

Operatory indeksowania i wycinków:

Operator Metoda
obj[key] __getitem__(self, key)
obj[key] = value __setitem__(self, key, value)
del obj[key] __delitem__(self, key)

Operatory unarne:

Operator Metoda
- __neg__(self)
+ __pos__(self)
abs() __abs__(self)
~ __invert__(self)

Operatory przypisania:

Operator Metoda
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)

Być może dlatego Python jest taki powolny – za każdym razem przed wykonaniem operatora szuka analogicznej funkcji w klasie i wszystkich jej klasach rodzicielskich. Ale to pozwala pisać najbardziej kompaktowy kod na świecie :)

6.3 Operator indeksacji

To, że można porównywać obiekty czy odejmować zbiory, jest w jakimś sensie oczywiste. Możesz sam się domyślić o tym, pisząc klasę, która zakłada na niej operacje logiczne lub matematyczne.

Chcę z Tobą przeanalizować taki ciekawy przykład – jak operator indeksacji. Zacznijmy od razu od przykładowego kodu:


class CustomList:
    def __init__(self, data):
        self.data = data
        
    def __getitem__(self, index):
        return self.data[index]
        
    def __setitem__(self, index, value):
        self.data[index] = value
        
    def __delitem__(self, index):
        del self.data[index]
        
    def __repr__(self):
        return repr(self.data)
        
# Użycie
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1])  # Wyświetli: 2
c_list[1] = 10
print(c_list)  # Wyświetli: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list)  # Wyświetli: [1, 3, 4, 5]

Tu widzimy przykład trzech operacji:

  • Odczyt danych przez indeks
  • Zapis danych przez indeks
  • A nawet usunięcie danych przez indeks.

A przecież wcale niekoniecznie, że dane wewnątrz będą przechowywane w formie listy. Lub indeks niekoniecznie musi być liczbą. Na przykład, klasa dictionary (słownik) jest właśnie tak zaimplementowana.

Tworzymy SuperList

Pamiętasz klasę list? Można jej przypisywać elementy, ale tylko te, które indeksy już są. Zróbmy swoją klasę, nazwiemy ją SuperList, do której elementów można będzie odnosić się po dowolnym indeksie:

  • Jeśli indeks < 0, to będziemy wstawiać element na początku
  • Jeśli indeks >= len, to będziemy dodawać element na końcu
  • W pozostałych przypadkach po prostu zwracamy element

Przykład:


class SuperList(list):
    def __init__(self, value):
        super().__init__(value)
        
    def __setitem__(self, index, value):
        if index >= len(self):
            super().append(value)
        elif index < 0:
            super().insert(0, value)
        else:
            super().__setitem__(index, value)
        
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst)  # [99, 1, 2, 3, 100]

Tak więc przeciążenie indeksów — świetna możliwość, i polecam korzystać z niej w praktyce. A na dzisiaj — to wszystko.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION