Szukaj…


Metody Magic / Dunder

Metody magiczne (zwane również dunder jako skrót od podwójnego podkreślenia) w Pythonie służą do podobnego celu, jak przeciążanie operatora w innych językach. Pozwalają klasie zdefiniować jej zachowanie, gdy jest używana jako operand w jednoargumentowych lub binarnych wyrażeniach operatora. Służą również jako implementacje wywoływane przez niektóre wbudowane funkcje.

Rozważ tę implementację wektorów dwuwymiarowych.

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)

Teraz można naturalnie używać wystąpień klasy Vector w różnych wyrażeniach.

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

Typy kontenerów i sekwencji

Możliwe jest emulowanie typów kontenerów, które obsługują dostęp do wartości według klucza lub indeksu.

Rozważ tę naiwną implementację rzadkiej listy, która przechowuje tylko niezerowe elementy w celu oszczędzania pamięci.

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

Następnie możemy użyć sparselist podobnie jak zwykłej 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

Typy wywoływalne

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

Postępowanie w przypadku niewdrożonego zachowania

Jeśli twoja klasa nie implementuje określonego przeciążonego operatora dla podanych typów argumentów, powinna return NotImplemented ( zwróć uwagę, że jest to specjalna stała , a nie taka sama jak NotImplementedError ). Pozwoli to Pythonowi wrócić do wypróbowania innych metod, aby operacja działała:

Po NotImplemented interpreter spróbuje następnie wykonać operację odbitą na innym typie lub inną operację zastępczą, w zależności od operatora. Jeśli wszystkie próby operacji zwrócą NotImplemented , interpreter zgłosi odpowiedni wyjątek.

Na przykład, biorąc pod uwagę x + y , jeśli x.__add__(y) zwraca niezaimplementowane, zamiast tego zostanie podjęta próba y.__radd__(x) .

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__

Ponieważ jest to odzwierciedlona metoda, musimy zaimplementować __add__ i __radd__ aby uzyskać oczekiwane zachowanie we wszystkich przypadkach; na szczęście, ponieważ obaj robią to samo w tym prostym przykładzie, możemy skorzystać ze skrótu.

W użyciu:

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

Przeciążenie operatora

Poniżej znajdują się operatory, które mogą być przeciążone w klasach, wraz z wymaganymi definicjami metod oraz przykład operatora używanego w wyrażeniu.

Uwaga: użycie other jako nazwy zmiennej nie jest obowiązkowe, ale jest uważane za normę.

Operator metoda Wyrażenie
+ Dodawanie __add__(self, other) a1 + a2
- Odejmowanie __sub__(self, other) a1 - a2
* Mnożenie __mul__(self, other) a1 * a2
@ Mnożenie macierzy __matmul__(self, other) a1 @ a2 ( Python 3.5 )
/ Division __div__(self, other) a1 / a2 ( tylko Python 2 )
/ Division __truediv__(self, other) a1 / a2 ( Python 3 )
// Podział podłogi __floordiv__(self, other) a1 // a2
% Modulo / Remainder __mod__(self, other) a1 % a2
** Moc __pow__(self, other[, modulo]) a1 ** a2
<< Przesunięcie bitowe w lewo __lshift__(self, other) a1 << a2
>> Przesunięcie bitowe w prawo __rshift__(self, other) a1 >> a2
& Bitowe AND __and__(self, other) a1 & a2
^ Bitowy XOR __xor__(self, other) a1 ^ a2
| (Bitowe LUB) __or__(self, other) a1 | a2
- Negacja (arytmetyka) __neg__(self) -a1
+ Pozytywne __pos__(self) +a1
~ NIE Bitowe __invert__(self) ~a1
< Mniej niż __lt__(self, other) a1 < a2
<= Mniejszy lub równy __le__(self, other) a1 <= a2
== Równa się __eq__(self, other) a1 == a2
!= Nie równy __ne__(self, other) a1 != a2
> Większy niż __gt__(self, other) a1 > a2
>= Większy lub równy __ge__(self, other) a1 >= a2
[index] Operator indeksu __getitem__(self, index) a1[index]
in In operator __contains__(self, other) a2 in a1
(*args, ...) Dzwonię __call__(self, *args, **kwargs) a1(*args, **kwargs)

Opcjonalny parametr modulo dla __pow__ jest używany tylko przez wbudowaną funkcję pow .


Każdy ze sposobów odpowiednich do operatora binarny ma odpowiadającą „prawy” metody, które zaczynają się __r , na przykład __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

a także odpowiednią wersję __i , zaczynając od __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

Ponieważ w tych metodach nie ma nic specjalnego, wiele innych części języka, części standardowej biblioteki, a nawet moduły innych firm dodają same magiczne metody, takie jak metody rzutowania obiektu na typ lub sprawdzania właściwości obiektu. Na przykład wbudowana funkcja str() wywołuje metodę __str__ obiektu, jeśli istnieje. Niektóre z tych zastosowań wymieniono poniżej.

Funkcjonować metoda Wyrażenie
Przesyłanie do int __int__(self) int(a1)
Funkcja absolutna __abs__(self) abs(a1)
Przesyłam na str __str__(self) str(a1)
Przesyłanie do unicode __unicode__(self) unicode(a1) (tylko Python 2)
Reprezentacja łańcucha __repr__(self) repr(a1)
Przesyłam na bool __nonzero__(self) bool(a1)
Formatowanie ciągów __format__(self, formatstr) "Hi {:abc}".format(a1)
Hashowanie __hash__(self) hash(a1)
Długość __len__(self) len(a1)
Wywrócony __reversed__(self) reversed(a1)
Piętro __floor__(self) math.floor(a1)
Sufit __ceil__(self) math.ceil(a1)

Istnieją również specjalne metody __enter__ i __exit__ dla menedżerów kontekstu i wiele innych.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow