Význam operátoru v Pythonu můžete změnit v závislosti na použitých operandech. V tomto kurzu se naučíte, jak používat přetížení operátorů v programování objektově orientovaného Pythonu.
Přetížení operátora Pythonu
Operátoři Pythonu pracují pro integrované třídy. Stejný operátor se ale chová odlišně u různých typů. Například +
operátor provede aritmetické sčítání na dvou číslech, sloučí dva seznamy nebo zřetězí dva řetězce.
Tato funkce v Pythonu, která umožňuje stejnému operátorovi mít jiný význam podle kontextu, se nazývá přetížení operátoru.
Co se tedy stane, když je použijeme s objekty třídy definované uživatelem? Uvažujme o následující třídě, která se pokusí simulovat bod ve 2D souřadném systému.
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)
Výstup
Traceback (poslední hovor poslední): Soubor "", řádek 9, v tisku (p1 + p2) TypeError: nepodporované typy operandů pro +: 'Point' a 'Point'
Tady vidíme, že a TypeError
bylo zvýšeno, protože Python nevěděl, jak přidat dva Point
objekty dohromady.
Této úlohy však můžeme v Pythonu dosáhnout přetížením operátora. Nejprve si ale pojďme představit speciální funkce.
Speciální funkce Pythonu
Funkce třídy, které začínají dvojitým podtržítkem, __
se v Pythonu nazývají speciální funkce.
Tyto funkce nejsou typické funkce, které definujeme pro třídu. __init__()
Funkci jsme definovali výše, je jedním z nich. Volá se pokaždé, když vytvoříme nový objekt této třídy.
V Pythonu existuje řada dalších speciálních funkcí. Navštivte speciální funkce Pythonu, kde se o nich dozvíte více.
Pomocí speciálních funkcí můžeme naši třídu kompatibilní s integrovanými funkcemi.
>>> p1 = Point(2,3) >>> print(p1)
Předpokládejme, že chceme, aby print()
funkce tiskla souřadnice Point
objektu namísto toho, co jsme dostali. V __str__()
naší třídě můžeme definovat metodu, která řídí, jak se objekt vytiskne. Pojďme se podívat, jak toho můžeme dosáhnout:
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)
Nyní zkusme print()
funkci znovu.
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)
Výstup
(2, 3)
To je lepší. Ukázalo se, že tato stejná metoda je vyvolána, když použijeme vestavěnou funkci str()
nebo format()
.
>>> str(p1) '(2,3)' >>> format(p1) '(2,3)'
Když tedy použijete str(p1)
nebo format(p1)
, Python interně volá p1.__str__()
metodu. Odtud název, speciální funkce.
Nyní se vraťme k přetížení operátora.
Přetížení operátora +
K přetížení +
operátora budeme muset implementovat __add__()
funkci ve třídě. S velkou mocí přichází i velká odpovědnost. V této funkci můžeme dělat, co se nám líbí. Je však rozumnější vrátit Point
objekt souřadnicového součtu.
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)
Nyní zkusme operaci přidání znovu:
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)
Výstup
(3,5)
Ve skutečnosti se stane, že když použijete p1 + p2
, volá Python, p1.__add__(p2)
což je naopak Point.__add__(p1,p2)
. Poté se provede operace přidání způsobem, který jsme zadali.
Podobně můžeme přetížit i další operátory. Speciální funkce, kterou musíme implementovat, je uvedena v tabulce níže.
Operátor | Výraz | Vnitřně |
---|---|---|
Přidání | p1 + p2 | p1.__add__(p2) |
Odčítání | p1 - p2 | p1.__sub__(p2) |
Násobení | p1 * p2 | p1.__mul__(p2) |
Napájení | p1 ** p2 | p1.__pow__(p2) |
Divize | p1 / p2 | p1.__truediv__(p2) |
Podlahové dělení | p1 // p2 | p1.__floordiv__(p2) |
Zbytek (modulo) | p1 % p2 | p1.__mod__(p2) |
Bitový posun vlevo | p1 << p2 | p1.__lshift__(p2) |
Bitový posun doprava | p1>> p2 | p1.__rshift__(p2) |
Bitové AND | p1 & p2 | p1.__and__(p2) |
Bitové NEBO | p1 | p2 | p1.__or__(p2) |
Bitový XOR | p1 p2 | p1.__xor__(p2) |
Bitové NENÍ | ~p1 | p1.__invert__() |
Operátoři porovnání přetížení
Python neomezuje přetížení operátorů pouze na aritmetické operátory. Můžeme také přetížit operátory porovnání.
Předpokládejme, že jsme chtěli do <
naší Point
třídy implementovat symbol menší než symbol .
Porovnejme velikost těchto bodů od počátku a pro tento účel vraťme výsledek. Lze jej implementovat následujícím způsobem.
# 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)