Python 3/Operaattoreiden kuormittaminen
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.
| 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) |
| 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ä]- 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.
| Johdanto: | |
|---|---|
| Tietotyypit ja tietorakenteet: |
Luvut - Merkkijonot - Lista - Monikko (tuple) - Sanakirja - Joukko (set) |
| Ohjausrakenteet | |
| Muut kielen rakenteet: |
Moduuli - Luokka - Funktio - Virheidenhallinta - Tiedosto |
| Graafinen käyttöliittymä: | |
| Harjoitustehtäviä: | |
| Lisätiedot ja lähteet: |