Python Language
overbelasting
Zoeken…
Magie / Dunder-methoden
Magie (ook dunder genoemd als afkorting voor dubbele onderstrepingstekens) methoden in Python hebben een vergelijkbaar doel als overbelasting van de operator in andere talen. Ze stellen een klasse in staat om zijn gedrag te definiëren wanneer het wordt gebruikt als een operand in uitdrukkingen van een unaire of binaire operator. Ze dienen ook als implementaties die door sommige ingebouwde functies worden aangeroepen.
Overweeg deze implementatie van tweedimensionale vectoren.
import math
class Vector(object):
# instantiation
def __init__(self, x, y):
self.x = x
self.y = y
# unary negation (-v)
def __neg__(self):
return Vector(-self.x, -self.y)
# addition (v + u)
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# subtraction (v - u)
def __sub__(self, other):
return self + (-other)
# equality (v == u)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# abs(v)
def __abs__(self):
return math.hypot(self.x, self.y)
# str(v)
def __str__(self):
return '<{0.x}, {0.y}>'.format(self)
# repr(v)
def __repr__(self):
return 'Vector({0.x}, {0.y})'.format(self)
Het is nu mogelijk om op natuurlijke wijze instanties van de Vector
klasse in verschillende uitdrukkingen te gebruiken.
v = Vector(1, 4)
u = Vector(2, 0)
u + v # Vector(3, 4)
print(u + v) # "<3, 4>" (implicit string conversion)
u - v # Vector(1, -4)
u == v # False
u + v == v + u # True
abs(u + v) # 5.0
Container- en sequentietypen
Het is mogelijk om containertypen te emuleren die toegang tot waarden per sleutel of index ondersteunen.
Overweeg deze naïeve implementatie van een schaarse lijst, die alleen zijn niet-nul elementen opslaat om geheugen te besparen.
class sparselist(object):
def __init__(self, size):
self.size = size
self.data = {}
# l[index]
def __getitem__(self, index):
if index < 0:
index += self.size
if index >= self.size:
raise IndexError(index)
try:
return self.data[index]
except KeyError:
return 0.0
# l[index] = value
def __setitem__(self, index, value):
self.data[index] = value
# del l[index]
def __delitem__(self, index):
if index in self.data:
del self.data[index]
# value in l
def __contains__(self, value):
return value == 0.0 or value in self.data.values()
# len(l)
def __len__(self):
return self.size
# for value in l: ...
def __iter__(self):
return (self[i] for i in range(self.size)) # use xrange for python2
Vervolgens kunnen we een sparselist
net als een normale list
.
l = sparselist(10 ** 6) # list with 1 million elements
0 in l # True
10 in l # False
l[12345] = 10
10 in l # True
l[12345] # 10
for v in l:
pass # 0, 0, 0, ... 10, 0, 0 ... 0
Oproepbare typen
class adder(object):
def __init__(self, first):
self.first = first
# a(...)
def __call__(self, second):
return self.first + second
add2 = adder(2)
add2(1) # 3
add2(2) # 4
Omgaan met niet-geïmplementeerd gedrag
Als je klas een specifieke overbelast operator voor de typen redeneringen niet uitvoert, moet het return NotImplemented
(Merk op dat dit een speciale constant , niet hetzelfde als NotImplementedError
). Hierdoor kan Python terugvallen op het proberen van andere methoden om de operatie te laten werken:
Wanneer
NotImplemented
wordt geretourneerd, zal de interpreter de gereflecteerde bewerking op het andere type of een andere fallback proberen, afhankelijk van de operator. Als alle gepoogde bewerkingenNotImplemented
retourneren, maakt de tolk een passende uitzondering.
Als bijvoorbeeld x + y
, als x.__add__(y)
niet-geïmplementeerd retourneert, wordt in plaats daarvan y.__radd__(x)
geprobeerd.
class NotAddable(object):
def __init__(self, value):
self.value = value
def __add__(self, other):
return NotImplemented
class Addable(NotAddable):
def __add__(self, other):
return Addable(self.value + other.value)
__radd__ = __add__
Omdat dit de weerspiegelde methode is, moeten we __add__
en __radd__
implementeren om in alle gevallen het verwachte gedrag te krijgen; gelukkig, omdat ze allebei hetzelfde doen in dit eenvoudige voorbeeld, kunnen we een kortere weg nemen.
In gebruik:
>>> x = NotAddable(1)
>>> y = Addable(2)
>>> x + x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'NotAddable' and 'NotAddable'
>>> y + y
<so.Addable object at 0x1095974d0>
>>> z = x + y
>>> z
<so.Addable object at 0x109597510>
>>> z.value
3
Overbelasting van de operator
Hieronder staan de operators die in klassen kunnen worden overladen, samen met de vereiste methodedefinities, en een voorbeeld van de operator die in een expressie wordt gebruikt.
NB Het gebruik van other
als variabelenaam is niet verplicht, maar wordt wel als norm beschouwd.
operator | Methode | Uitdrukking |
---|---|---|
+ Toevoeging | __add__(self, other) | a1 + a2 |
- Aftrekken | __sub__(self, other) | a1 - a2 |
* Vermenigvuldiging | __mul__(self, other) | a1 * a2 |
@ Matrixvermenigvuldiging | __matmul__(self, other) | a1 @ a2 ( Python 3.5 ) |
/ Divisie | __div__(self, other) | a1 / a2 ( alleen Python 2 ) |
/ Divisie | __truediv__(self, other) | a1 / a2 ( Python 3 ) |
// Floor Division | __floordiv__(self, other) | a1 // a2 |
% Modulo / Rest | __mod__(self, other) | a1 % a2 |
** vermogen | __pow__(self, other[, modulo]) | a1 ** a2 |
<< Bitwise Left Shift | __lshift__(self, other) | a1 << a2 |
>> Bitwise Right Shift | __rshift__(self, other) | a1 >> a2 |
& Bitwise AND | __and__(self, other) | a1 & a2 |
^ Bitgewijs XOR | __xor__(self, other) | a1 ^ a2 |
| (Bitwise OF) | __or__(self, other) | a1 | a2 |
- Ontkenning (rekenen) | __neg__(self) | -a1 |
+ Positief | __pos__(self) | +a1 |
~ Bitwise NIET | __invert__(self) | ~a1 |
< Minder dan | __lt__(self, other) | a1 < a2 |
<= Kleiner dan of gelijk aan | __le__(self, other) | a1 <= a2 |
== gelijk aan | __eq__(self, other) | a1 == a2 |
!= Niet gelijk aan | __ne__(self, other) | a1 != a2 |
> Groter dan | __gt__(self, other) | a1 > a2 |
>= Groter dan of gelijk aan | __ge__(self, other) | a1 >= a2 |
[index] Indexoperator | __getitem__(self, index) | a1[index] |
in operator | __contains__(self, other) | a2 in a1 |
(*args, ...) Bellen | __call__(self, *args, **kwargs) | a1(*args, **kwargs) |
De optionele parameter modulo
voor __pow__
wordt alleen gebruikt door de ingebouwde functie pow
.
Elk van de methoden die overeenkomen met een binaire operator heeft een overeenkomstige "juiste" methode die begint met __r
, bijvoorbeeld __radd__
:
class A:
def __init__(self, a):
self.a = a
def __add__(self, other):
return self.a + other
def __radd__(self, other):
print("radd")
return other + self.a
A(1) + 2 # Out: 3
2 + A(1) # prints radd. Out: 3
evenals een overeenkomstige inplace-versie, beginnend met __i
:
class B:
def __init__(self, b):
self.b = b
def __iadd__(self, other):
self.b += other
print("iadd")
return self
b = B(2)
b.b # Out: 2
b += 1 # prints iadd
b.b # Out: 3
Omdat er niets bijzonders is aan deze methoden, voegen veel andere delen van de taal, delen van de standaardbibliotheek en zelfs modules van derden zelf magische methoden toe, zoals methoden om een object naar een type te casten of eigenschappen van het object te controleren. De functie ingebouwde str()
roept bijvoorbeeld de methode __str__
het object aan, als deze bestaat. Sommige van deze toepassingen worden hieronder vermeld.
Functie | Methode | Uitdrukking |
---|---|---|
Casting naar int | __int__(self) | int(a1) |
Absolute functie | __abs__(self) | abs(a1) |
Casting naar str | __str__(self) | str(a1) |
Casten naar unicode | __unicode__(self) | unicode(a1) (alleen Python 2) |
Stringvoorstelling | __repr__(self) | repr(a1) |
Casten naar bool | __nonzero__(self) | bool(a1) |
Stringopmaak | __format__(self, formatstr) | "Hi {:abc}".format(a1) |
hashing | __hash__(self) | hash(a1) |
Lengte | __len__(self) | len(a1) |
Omgekeerd | __reversed__(self) | reversed(a1) |
Verdieping | __floor__(self) | math.floor(a1) |
Plafond | __ceil__(self) | math.ceil(a1) |
Er zijn ook de speciale methoden __enter__
en __exit__
voor contextbeheerders en nog veel meer.