Python - Operatera preopterećenja (Operator Overloading)


Preopterećenje operatora Pythona

Python operateri rade za ugrađene klase. Ali isti se operator ponaša različito s različitim tipovima. Na primjer, operator + izvršiće aritmetičko sabiranje dva broja, objediniti dvije liste ili spojiti dva stringa.

Ova karakteristika u Pythonu omogućava istom operatoru da ima različita značenja u skladu s kontekstom naziva se operator overloading. Pa što se događa kada ih koristimo s objektima korisnički definisanih klasa? Razmotrimo sljedeću klasu koja pokušava simulirati točku u 2-D koordinatnom sistemu.

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)

Ovdje možemo vidjeti da je podignuta TypeError, jer Python nije znao kako dodati dva Point objekta zajedno. Međutim, ovaj zadatak u Pythonu možemo postići preopterećenjem operatora (operator overloading). Ali prvo, hajde da dobijemo pojam o posebnim funkcijama.



Python posebne funkcije

Funkcije klase koje počinju dvostrukim donjim podvlačenjem __ nazivaju se posebnim funkcijama u Pythonu. Te funkcije nisu tipične funkcije koje definišemo za klasu. Funkcija __init__() koju smo gore definisali jedna je od njih. Pozva se svaki put kada kreiramo novi objekt te klase.

Postoje brojne druge posebne funkcije u Pythonu. Posjetite Python Special Functions da biste saznali više o njima. Koristeći posebne funkcije, možemo učiniti našu klasu kompatibilnom s ugrađenim funkcijama.

>>> p1 = Point(2,3)
>>> print(p1)
<__main__.Point object at 0x00000000031F8CC0>

Pretpostavimo da želimo da funkcija print() ispisuje koordinate objekta Point umjesto onoga što smo dobili. Možemo definirati metodu __str__() u našoj klasi koja kontroliše kako se objekt ispisuje. Pogledajmo kako to možemo postići:

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)

Pokušajmo ponovo s funkcijom print().

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)

To je bolje. Ispostavilo se da se ta ista metoda poziva kada koristimo ugrađenu funkciju str() ili format().

>>> str(p1)
'(2,3)'

>>> format(p1)
'(2,3)'

Dakle, kada koristite str(p1) ili format(p1), Python interno poziva metodu p1.__str__ (). Otuda i naziv, posebne funkcije. Vratimo se sada preopterećenju operatera (operator overloading).



Preopterećenje operatora + (Overloading the + Operator)

Da bismo preopteretili + operator, trebaćemo implementovati funkciju __add__() u klasi. S velikom moći dolazi i velika odgovornost. Unutar ove funkcije možemo raditi što god želimo. Ali razumnije je vratiti bodovni objekt koordinatnog zbira.

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)

Pokušajmo ponovo sa sabiranjem:

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)

Ono što se zapravo događa je da, kada koristite p1 + p2, Python poziva p1.__add__(p2) što je zauzvrat Point.__ add__ (p1, p2). Nakon toga, operacija sabiranja izvodi se na način koji smo odredili. Slično tome, možemo preopteretiti i druge operatore. Posebna funkcija koju moramo implementovati je data u sljedećoj tabeli.

Operator Izraz Interno
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__()


Preopterećenje operatora poređenja

Python ne ograničava preopterećenje operatora samo na aritmetičke operatore. Možemo preopteretiti i operatore poređenja. Pretpostavimo da smo željeli implementovati simbol manji od < u našu Point klasu. Poređenjem veličine ovih bodova iz izvorište i vratimo rezultat u tu svrhu. Može se implementovati na sljedeći način.

# preopterećenje operatora manjeg od
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)

# koristite manje od
print(p1<p2)
print(p2<p3)
print(p1<p3)

Slično tome, posebne funkcije koje trebamo implementovati za preopterećenje ostalih operatora upoređivanja navedene su u sljedećoj tabeli.

Operator Izraz Interno
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)