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 bewerkingen NotImplemented 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.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow