Python Language
Überlastung
Suche…
Magie / Dunder-Methoden
Magic-Methoden (auch als Dunder als Abkürzung für doppelten Unterstrich bezeichnet) dienen in Python einem ähnlichen Zweck wie das Überladen von Operatoren in anderen Sprachen. Sie erlauben einer Klasse, ihr Verhalten zu definieren, wenn sie als Operand in unären oder binären Operatorausdrücken verwendet wird. Sie dienen auch als Implementierungen, die von einigen integrierten Funktionen aufgerufen werden.
Betrachten Sie diese Implementierung von zweidimensionalen Vektoren.
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)
Nun ist es natürlich möglich, Instanzen der Vector
Klasse in verschiedenen Ausdrücken zu verwenden.
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- und Sequenztypen
Es ist möglich, Containertypen zu emulieren, die den Zugriff auf Werte nach Schlüssel oder Index unterstützen.
Betrachten Sie diese naive Implementierung einer spärlichen Liste, die nur die von Null verschiedenen Elemente speichert, um Speicherplatz zu sparen.
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
Dann können wir eine sparselist
ähnlich einer regulären 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
Aufrufbare 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
Umgang mit nicht implementiertem Verhalten
Wenn Ihre Klasse keinen bestimmten überladenen Operator für die angegebenen Argumenttypen implementiert, sollte return NotImplemented
( beachten Sie, dass dies eine spezielle Konstante ist , die nicht mit NotImplementedError
). Dadurch kann Python auf andere Methoden zurückgreifen, um die Operation zum Laufen zu bringen:
Wenn
NotImplemented
zurückgegeben wird, versucht der Interpreter je nach Operator die reflektierte Operation mit dem anderen Typ oder einen anderen Fallback. Wenn alle versuchten OperationenNotImplemented
,NotImplemented
der Interpreter eine entsprechende Ausnahme aus.
Wenn Sie beispielsweise x + y
angeben, wenn x.__add__(y)
unimplementiert zurückgegeben wird, wird stattdessen y.__radd__(x)
versucht.
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__
Da dies die reflektierte Methode ist, müssen wir __add__
und __radd__
implementieren, um das erwartete Verhalten in allen Fällen zu erhalten. Zum Glück können wir, da beide in diesem einfachen Beispiel dasselbe tun, eine Abkürzung nehmen.
In Benutzung:
>>> 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
Überlastung des Bedieners
Nachfolgend sind die Operatoren aufgeführt, die in Klassen überladen werden können, zusammen mit den erforderlichen Methodendefinitionen sowie ein Beispiel für den in einem Ausdruck verwendeten Operator.
Anmerkung: Die Verwendung other
als Variablenname ist nicht obligatorisch, wird jedoch als Norm betrachtet.
Operator | Methode | Ausdruck |
---|---|---|
+ Zusatz | __add__(self, other) | a1 + a2 |
- Subtraktion | __sub__(self, other) | a1 - a2 |
* Multiplikation | __mul__(self, other) | a1 * a2 |
@ Matrixmultiplikation | __matmul__(self, other) | a1 @ a2 ( Python 3.5 ) |
/ Abteilung | __div__(self, other) | a1 / a2 (nur Python 2 ) |
/ Abteilung | __truediv__(self, other) | a1 / a2 ( Python 3 ) |
// Floor Division | __floordiv__(self, other) | a1 // a2 |
% Modulo / Rest | __mod__(self, other) | a1 % a2 |
** Power | __pow__(self, other[, modulo]) | a1 ** a2 |
<< Bitweise Linksverschiebung | __lshift__(self, other) | a1 << a2 |
>> Bitweise Rechtsverschiebung | __rshift__(self, other) | a1 >> a2 |
& Bitweises AND | __and__(self, other) | a1 & a2 |
^ Bitweises XOR | __xor__(self, other) | a1 ^ a2 |
| (Bitweises ODER) | __or__(self, other) | a1 | a2 |
- Negation (Arithmetik) | __neg__(self) | -a1 |
+ Positiv | __pos__(self) | +a1 |
~ Bitweise NICHT | __invert__(self) | ~a1 |
< Weniger als | __lt__(self, other) | a1 < a2 |
<= Weniger als oder gleich | __le__(self, other) | a1 <= a2 |
== Gleich | __eq__(self, other) | a1 == a2 |
!= Nicht gleich | __ne__(self, other) | a1 != a2 |
> Größer als | __gt__(self, other) | a1 > a2 |
>= Größer als oder gleich | __ge__(self, other) | a1 >= a2 |
[index] Indexoperator | __getitem__(self, index) | a1[index] |
in in operator | __contains__(self, other) | a2 in a1 |
(*args, ...) Anrufen | __call__(self, *args, **kwargs) | a1(*args, **kwargs) |
Der optionale Parameter modulo
für __pow__
wird nur von der integrierten pow
-Funktion verwendet.
Jede der Methoden, die einem binären Operator entsprechen, hat eine entsprechende "rechte" Methode, die mit __r
, beispielsweise __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
sowie eine entsprechende Inplace-Version, beginnend mit __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
Da diese Methoden nichts Besonderes sind, fügen viele andere Teile der Sprache, Teile der Standardbibliothek und sogar Module von Drittanbietern selbst magische Methoden hinzu, beispielsweise Methoden, um ein Objekt in einen Typ umzuwandeln oder die Eigenschaften des Objekts zu überprüfen. Die eingebaute Funktion str()
ruft beispielsweise die __str __str__
Methode des Objekts auf, sofern vorhanden. Einige dieser Anwendungen sind nachfolgend aufgeführt.
Funktion | Methode | Ausdruck |
---|---|---|
Casting auf int | __int__(self) | int(a1) |
Absolute Funktion | __abs__(self) | abs(a1) |
Casting auf str | __str__(self) | str(a1) |
Casting zu unicode | __unicode__(self) | unicode(a1) (nur Python 2) |
String-Darstellung | __repr__(self) | repr(a1) |
Casting zu bool | __nonzero__(self) | bool(a1) |
String-Formatierung | __format__(self, formatstr) | "Hi {:abc}".format(a1) |
Hashing | __hash__(self) | hash(a1) |
Länge | __len__(self) | len(a1) |
Rückgängig gemacht | __reversed__(self) | reversed(a1) |
Fußboden | __floor__(self) | math.floor(a1) |
Decke | __ceil__(self) | math.ceil(a1) |
Es gibt auch die speziellen Methoden __enter__
und __exit__
für Kontextmanager und viele andere.