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 Operationen NotImplemented , 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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow