Możesz zmienić znaczenie operatora w Pythonie w zależności od użytych operandów. Z tego samouczka dowiesz się, jak używać przeciążania operatorów w programowaniu obiektowym w języku Python.
Przeciążanie operatorów języka Python
Operatory Pythona działają dla klas wbudowanych. Ale ten sam operator zachowuje się inaczej w przypadku różnych typów. Na przykład +
operator wykona arytmetyczne dodawanie dwóch liczb, połączy dwie listy lub połączy dwa ciągi.
Ta funkcja w Pythonie, która pozwala temu samemu operatorowi mieć różne znaczenie w zależności od kontekstu, nazywana jest przeciążeniem operatora.
Więc co się dzieje, gdy używamy ich z obiektami klasy zdefiniowanej przez użytkownika? Rozważmy następującą klasę, która próbuje zasymulować punkt w 2-D układzie współrzędnych.
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y p1 = Point(1, 2) p2 = Point(2, 3) print(p1+p2)
Wynik
Traceback (ostatnie wywołanie ostatnie): Plik „”, wiersz 9, w druku (p1 + p2) TypeError: nieobsługiwane typy operandów dla +: 'Point' i 'Point'
Tutaj widzimy, że TypeError
podniesiono a, ponieważ Python nie wiedział, jak dodać do siebie dwa Point
obiekty.
Jednak zadanie to możemy osiągnąć w Pythonie poprzez przeciążenie operatorów. Ale najpierw przyjmijmy pojęcie o funkcjach specjalnych.
Funkcje specjalne Pythona
Funkcje klas rozpoczynające się od podwójnego podkreślenia __
nazywane są w Pythonie funkcjami specjalnymi.
Te funkcje nie są typowymi funkcjami, które definiujemy dla klasy. __init__()
Funkcja określiliśmy powyżej jest jednym z nich. Jest wywoływana za każdym razem, gdy tworzymy nowy obiekt tej klasy.
W Pythonie jest wiele innych funkcji specjalnych. Odwiedź funkcje specjalne języka Python, aby dowiedzieć się więcej o nich.
Korzystając z funkcji specjalnych, możemy uczynić naszą klasę kompatybilną z funkcjami wbudowanymi.
>>> p1 = Point(2,3) >>> print(p1)
Załóżmy, że chcemy, aby print()
funkcja wypisała współrzędne Point
obiektu zamiast tego, co otrzymaliśmy. __str__()
W naszej klasie możemy zdefiniować metodę, która kontroluje sposób drukowania obiektu. Spójrzmy, jak możemy to osiągnąć:
class Point: def __init__(self, x = 0, y = 0): self.x = x self.y = y def __str__(self): return "((0),(1))".format(self.x,self.y)
Teraz spróbujmy print()
ponownie tej funkcji.
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "((0), (1))".format(self.x, self.y) p1 = Point(2, 3) print(p1)
Wynik
(2, 3)
Tak lepiej. Okazuje się, że ta sama metoda jest wywoływana, gdy używamy funkcji wbudowanej str()
lub format()
.
>>> str(p1) '(2,3)' >>> format(p1) '(2,3)'
Tak więc, gdy używasz str(p1)
or format(p1)
, Python wewnętrznie wywołuje p1.__str__()
metodę. Stąd nazwa, funkcje specjalne.
Wróćmy teraz do przeciążenia operatora.
Przeciążanie operatora +
Aby przeciążyć +
operatora, będziemy musieli zaimplementować __add__()
funkcję w klasie. Z dużą mocą przychodzi duża odpowiedzialność. W ramach tej funkcji możemy robić, co nam się podoba. Ale rozsądniej jest zwrócić Point
obiekt sumy współrzędnych.
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "((0),(1))".format(self.x, self.y) def __add__(self, other): x = self.x + other.x y = self.y + other.y return Point(x, y)
Teraz spróbujmy ponownie dodać operację:
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "((0),(1))".format(self.x, self.y) def __add__(self, other): x = self.x + other.x y = self.y + other.y return Point(x, y) p1 = Point(1, 2) p2 = Point(2, 3) print(p1+p2)
Wynik
(3,5)
What actually happens is that, when you use p1 + p2
, Python calls p1.__add__(p2)
which in turn is Point.__add__(p1,p2)
. After this, the addition operation is carried out the way we specified.
Similarly, we can overload other operators as well. The special function that we need to implement is tabulated below.
Operator | Expression | Internally |
---|---|---|
Addition | p1 + p2 | p1.__add__(p2) |
Subtraction | p1 - p2 | p1.__sub__(p2) |
Multiplication | p1 * p2 | p1.__mul__(p2) |
Power | p1 ** p2 | p1.__pow__(p2) |
Division | p1 / p2 | p1.__truediv__(p2) |
Floor Division | p1 // p2 | p1.__floordiv__(p2) |
Remainder (modulo) | p1 % p2 | p1.__mod__(p2) |
Bitwise Left Shift | p1 << p2 | p1.__lshift__(p2) |
Bitwise Right Shift | p1>> p2 | p1.__rshift__(p2) |
Bitwise AND | p1 & p2 | p1.__and__(p2) |
Bitwise OR | p1 | p2 | p1.__or__(p2) |
Bitwise XOR | p1 p2 | p1.__xor__(p2) |
Bitwise NOT | ~p1 | p1.__invert__() |
Overloading Comparison Operators
Python does not limit operator overloading to arithmetic operators only. We can overload comparison operators as well.
Suppose we wanted to implement the less than symbol <
symbol in our Point
class.
Let us compare the magnitude of these points from the origin and return the result for this purpose. It can be implemented as follows.
# overloading the less than operator class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "((0),(1))".format(self.x, self.y) def __lt__(self, other): self_mag = (self.x ** 2) + (self.y ** 2) other_mag = (other.x ** 2) + (other.y ** 2) return self_mag < other_mag p1 = Point(1,1) p2 = Point(-2,-3) p3 = Point(1,-1) # use less than print(p1
Output
True False False
Similarly, the special functions that we need to implement, to overload other comparison operators are tabulated below.
Operator Expression Internally
Less than p1 < p2
p1.__lt__(p2)
Less than or equal to p1 <= p2
p1.__le__(p2)
Equal to p1 == p2
p1.__eq__(p2)
Not equal to p1 != p2
p1.__ne__(p2)
Greater than p1> p2
p1.__gt__(p2)
Greater than or equal to p1>= p2
p1.__ge__(p2)