Python Language
överbelastning
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 returnerarNotImplemented
, 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.