Sök…


Magiska / Dunder-metoder

Magi (även kallad dunder som en förkortning för dubbel understruk) -metoder i Python tjänar ett liknande syfte som operatörens överbelastning på andra språk. De tillåter en klass att definiera dess beteende när den används som operand i unary eller binär operatörsuttryck. De fungerar också som implementeringar som kallas av vissa inbyggda funktioner.

Överväg denna implementering av tvådimensionella vektorer.

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)

Nu är det möjligt att naturligt använda instanser av Vector klassen i olika uttryck.

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

Behållare och sekvenstyper

Det är möjligt att emulera containertyper som stöder åtkomstvärden med nyckel eller index.

Tänk på detta naiva genomförande av en gles lista, som bara lagrar dess icke-nollelement för att spara minne.

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

Sedan kan vi använda en sparselist ungefär som en vanlig 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

Tänkbara typer

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

Hantera oimplementerat beteende

Om din klass inte implementerar en specifik överbelastad operatör för de angivna argumenttyperna bör den return NotImplemented ( Observera att detta är en speciell konstant , inte samma som NotImplementedError ). Detta gör att Python kan falla tillbaka till att testa andra metoder för att få operationen att fungera:

När NotImplemented returneras kommer tolkaren att prova den reflekterade operationen på den andra typen, eller någon annan fallback, beroende på operatören. Om alla försökt operationer returnerar NotImplemented , kommer tolken att höja ett lämpligt undantag.

Till exempel, givet x + y , om x.__add__(y) returnerar oimplementerat, y.__radd__(x) istället.

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__

Eftersom detta är den reflekterade metoden måste vi implementera __add__ och __radd__ att få det förväntade beteendet i alla fall; Lyckligtvis, eftersom de båda gör samma sak i detta enkla exempel, kan vi ta en genväg.

I användning:

>>> 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

Operatörens överbelastning

Nedan visas operatörerna som kan överbelastas i klasser, tillsammans med de metoddefinitioner som krävs, och ett exempel på operatören som används i ett uttryck.

OBS! Användning av other som variabelnamn är inte obligatoriskt utan anses vara normen.

Operatör Metod Uttryck
+ Tillägg __add__(self, other) a1 + a2
- Subtraktion __sub__(self, other) a1 - a2
* Multiplikation __mul__(self, other) a1 * a2
@ Matrix Multiplikation __matmul__(self, other) a1 @ a2 ( Python 3.5 )
/ Division __div__(self, other) a1 / a2 (endast Python 2 )
/ Division __truediv__(self, other) a1 / a2 ( Python 3 )
// Golvavdelning __floordiv__(self, other) a1 // a2
% Modulo / Rest __mod__(self, other) a1 % a2
** Kraft __pow__(self, other[, modulo]) a1 ** a2
<< Bitvis vänsterskift __lshift__(self, other) a1 << a2
>> Bitvis högerväxling __rshift__(self, other) a1 >> a2
& Bitvis OCH __and__(self, other) a1 & a2
^ Bitvis XOR __xor__(self, other) a1 ^ a2
| (Bitvis ELLER) __or__(self, other) a1 | a2
- Negation (aritmetik) __neg__(self) -a1
+ Positiv __pos__(self) +a1
~ Bitvis INTE __invert__(self) ~a1
< Mindre än __lt__(self, other) a1 < a2
<= Mindre än eller lika med __le__(self, other) a1 <= a2
== lika med __eq__(self, other) a1 == a2
!= Inte lika med __ne__(self, other) a1 != a2
> Större än __gt__(self, other) a1 > a2
>= Större än eller lika med __ge__(self, other) a1 >= a2
[index] Indexoperatör __getitem__(self, index) a1[index]
in operatör __contains__(self, other) a2 in a1
(*args, ...) Ringer __call__(self, *args, **kwargs) a1(*args, **kwargs)

Den valfri parameter modulo för __pow__ används endast av pow inbyggd funktion.


Var och en av metoderna som motsvarar en binär operatör har en motsvarande "rätt" metod som börjar med __r , till exempel __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

såväl som en motsvarande version på plats, börjar med __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

Eftersom det inte finns något speciellt med dessa metoder, lägger många andra delar av språket, delar av standardbiblioteket och till och med tredjepartsmoduler magiska metoder på egen hand, som metoder för att kasta ett objekt till en typ eller kontrollera objektets egenskaper. Till exempel kallar den inbyggda str() -funktionen objektets __str__ metod, om den finns. Vissa av dessa användningar listas nedan.

Fungera Metod Uttryck
Gjutning till int __int__(self) int(a1)
Absolut funktion __abs__(self) abs(a1)
Gjutning till str __str__(self) str(a1)
Gjutning till unicode __unicode__(self) unicode(a1) (endast Python 2)
Strängrepresentation __repr__(self) repr(a1)
Gjutning till bool __nonzero__(self) bool(a1)
Strängformatering __format__(self, formatstr) "Hi {:abc}".format(a1)
hashning __hash__(self) hash(a1)
Längd __len__(self) len(a1)
Omvänd __reversed__(self) reversed(a1)
Golv __floor__(self) math.floor(a1)
Tak __ceil__(self) math.ceil(a1)

Det finns också de speciella metoderna __enter__ och __exit__ för __exit__ , och många fler.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow