Python 3/Operaattoreiden kuormittaminen

Wikikirjastosta


Operaattoreiden kuormittaminen tarkoittaa operandien väliin tai eteen laitettavien operaattoreiden, kuten +, -, *, /, toteuttamista omille luokille. Olemme jo nähneet miten operaattoria + voi käyttää paitsi numeroiden yhteenlaskuun myös yhdistämään kaksi listaa tai merkkijonoa ja operaattoreita * ja ** voi käyttää paitsi kertolaskuun ja potensiin korottamiseen myös monikon ja sanakirjan hajottamiseen funktion argumenteiksi.

Operaattori kuormitetaan toteuttamalla omassa oliossa erityinen metodi.

Kuormitettavat operaattorit
operaattori metodi
+ __add__(self, other)
__sub__(self, other)
* __mul__(self, other)
/ __truediv__(self, other)
// __floordiv__(self, other)
% __mod__(self, other)
** __pow__(self, other)
@ __matmul__(self, other)
>> __rshift__(self, other)
<< __lshift__(self, other)
& __and__(self, other)
| __or__(self, other)
^ __xor__(self, other)
< __lt__(self, other)
> __gt__(self, other)
<= __le__(self, other)
>= __ge__(self, other)
== __eq__(self, other)
!= __ne__(self, other)
__neg__(self)
+ __pos__(self)
~ __invert__(self)
in __contains__(self, key)
Kuormitettavat operaattorit
operaattori metodi
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)
>>= __irshift__(self, other)
<<= __ilshift__(self, other)
&= __iand__(self, other)
= __ior__(self, other)
^= __ixor__(self, other)

Binaarisista operaattoresta on olemassa myös käänteinen versio, jossa nimen eteen tulee r.

Loogisia operaattoreita and, or ja not ei voi kuormittaa.

Esimerkki vertailuoperaattoreiden kuormittamisesta[muokkaa | muokkaa wikitekstiä]

Tässä teemme tyypin kps, jolla voi esittää kivi–paperi–sakset-leikin arvoja ja vertailla niitä keskenään.

class kps:
    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return (self.val == other.val)

    def __gt__(self, other):
        if self.val == "paperi" and other.val == "kivi":
            return True

        if self.val == "sakset" and other.val == "paperi":
            return True

        if self.val == "kivi" and other.val == "sakset":
            return True

        return False

    def __ge__(self, other):
        return self.__eq__(other) or self.__gt__(other)

    def __lt__(self, other):
        if self.val == "kivi" and other.val == "paperi":
            return True

        if self.val == "paperi" and other.val == "sakset":
            return True

        if self.val == "sakset" and other.val == "kivi":
            return True

        return False

    def __le__(self, other):
        return self.__eq__(other) or self.__lt__(other)

    def __repr__(self):
        return f'{self.val}'


kivi = kps("kivi")
paperi = kps("paperi")
sakset = kps("sakset")

if __name__ == "__main__":
    from itertools import combinations_with_replacement

    units = [kivi, paperi, sakset]
    pairs = [x for x in combinations_with_replacement(units, 2)]

    print("==")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, "==", b)
        print(a == b)
        print()

    print("!=")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, "!=", b)
        print(a != b)
        print()

    print(">")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, ">", b)
        print(a > b)
        print()

    print("<")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, "<", b)
        print(a < b)
        print()

    print(">=")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, ">=", b)
        print(a >= b)
        print()

    print("<=")
    for pair in pairs:
        a = pair[0]
        b = pair[1]
        print(a, "<=", b)
        print(a <= b)
        print()

Tulostaa

==
kivi == kivi
True

kivi == paperi
False

kivi == sakset
False

paperi == paperi
True

paperi == sakset
False

sakset == sakset
True

!=
kivi != kivi
False

kivi != paperi
True

kivi != sakset
True

paperi != paperi
False

paperi != sakset
True

sakset != sakset
False

>
kivi > kivi
False

kivi > paperi
False

kivi > sakset
True

paperi > paperi
False

paperi > sakset
False

sakset > sakset
False

<
kivi < kivi
False

kivi < paperi
True

kivi < sakset
False

paperi < paperi
False

paperi < sakset
True

sakset < sakset
False

>=
kivi >= kivi
True

kivi >= paperi
False

kivi >= sakset
True

paperi >= paperi
True

paperi >= sakset
False

sakset >= sakset
True

<=
kivi <= kivi
True

kivi <= paperi
True

kivi <= sakset
False

paperi <= paperi
True

paperi <= sakset
True

sakset <= sakset
True

Esimerkki bittioperaatoiden kuormittamisesta[muokkaa | muokkaa wikitekstiä]

Alla olevassa esimerkissä teemme kolmiarvoista totuusarvoa esittävän luokan, jossa tosi- ja epätosi-arvojen lisäksi on epätietoisuutta esittävä arvo (?). Koska and-, or- ja not-operaattoreita ei voi kuormittaa käytämme niiden sijasta &-, |- ja ~-operaattoreita. Metodia __repr__ käsiteltiin osassa Luokka.

class tri:
    def __init__(self, val):
        self.__val = val

    @property
    def val(self):
        return self.__val

    def __and__(self, other):
        if self.val == "0" or other.val == "0":
            return tri("0")
        if self.val == "?" or other.val == "?":
            return tri("?")
        else:
            return tri("1")

    def __or__(self, other):
        if self.val == "1" or other.val == "1":
            return tri("1")
        elif self.val == "?" or other.val == "?":
            return tri("?")
        else:
            return tri("0")

    def __invert__(self):
        if self.val == "?":
            return tri("?")
        elif self.val == "1":
            return tri("0")
        else:
            return tri("1")

    def __repr__(self):
        return f"tri('{self.__val}')"


if __name__ == "__main__":
    from itertools import combinations_with_replacement
    values = [tri("0"), tri("?"), tri("1")]
    pairs = [x for x in combinations_with_replacement(values, 2)]

    print(f"{'NOT': ^21}")
    for a in values:
        print("~", a, "=", ~a)

    print()
    print(f"{'AND': ^30}")
    for a, b in pairs:
        print(a, "&", b, "=", a & b)

    print()
    print(f"{'OR': ^30}")
    for a, b in pairs:
        print(a, "|", b, "=", a | b)

Tulostaa

         NOT         
~ tri('0') = tri('1')
~ tri('?') = tri('?')
~ tri('1') = tri('0')

             AND              
tri('0') & tri('0') = tri('0')
tri('0') & tri('?') = tri('0')
tri('0') & tri('1') = tri('0')
tri('?') & tri('?') = tri('?')
tri('?') & tri('1') = tri('?')
tri('1') & tri('1') = tri('1')

              OR              
tri('0') | tri('0') = tri('0')
tri('0') | tri('?') = tri('?')
tri('0') | tri('1') = tri('1')
tri('?') | tri('?') = tri('?')
tri('?') | tri('1') = tri('1')
tri('1') | tri('1') = tri('1')

Harjoitukset[muokkaa | muokkaa wikitekstiä]

  1. Tee murtolukua esittävä luokka ja toteuta sille ainakin operaattorit +, -, * ja /. Voit käyttä sieventämisessä apuna math-moduulin gcd-funktiota, joka palauttaa kahden annetun luvun suurimman yhteisen tekijän. Murtoluvuilla laskemista on käsitelty Matematiikan kirjassa.