―― Neulich hatte ich die Gelegenheit, auf der PyCon JP 2016 bekannt zu geben, also habe ich es angekündigt.
-Welche Methode hat Vorrang vor Objekt A, das ** __ add__
** definiert, oder Objekt B, das ** __radd__
** definiert?
class A:
def __init__(self, value):
self.value = value
def __add__(self, other):
self.value += other.value
return self
class B:
def __init__(self, value):
self.value = value
def __radd__(self, other):
self.value += other.value
return self
A(1) + B(2) #=>Von A.__add__Funktioniert es? B's__radd__Funktioniert es?
A .__ add__
** auf der linken Seite hat Priorität und wird ausgeführt.__radd__
** eine Methode für den Fallback zu sein, wenn ** __ add__
** nicht implementiert ist.
――Ich habe diesen Bereich nicht richtig verstanden, deshalb habe ich eine falsche Erklärung gegeben. Entschuldigung.A(1) + B(2) #=> A(3)
#=>Für eine Klasse__add__Ist implementiert, so funktioniert es zuerst
In Dokument enthält die Erklärung der Systemmethode ** __ rxxx__
** die folgende Erklärung. ..
Beim Aufrufen dieser Methoden wurde der Operator der Binomialarithmetikoperation reflektiert(Wurde ersetzt)Sachen umsetzen.
Diese Funktionen werden nur aufgerufen, wenn der On-Operator auf der linken Seite die entsprechende Operation nicht unterstützt und die Nicht-Operatoren unterschiedlichen Typs sind.
Zum Beispiel ist y__rsub__()Wenn es sich um eine Instanz einer Klasse mit einer Methode handelt
Ausdruck x-Wenn y ausgewertet wird, ist x.__sub__(y)Y, wenn NotImplemented zurückgegeben wird.__rsub__(x)Wird genannt.
# refs http://docs.python.jp/3/reference/datamodel.html#object.__ror__
Es wird auch als Anmerkung wie folgt geschrieben.
Hinweis Der Typ des Operators auf der rechten Seite ist eine Unterklasse des Typs des Operators auf der linken Seite.
Wenn für diese Unterklassenmethode eine Reflexionsmethode definiert ist
Diese Methode wird aufgerufen, bevor die nicht reflektierende Methode des Operators auf der linken Seite aufgerufen wird.
Dieses Verhalten ermöglicht es Unterklassen, die Operation des übergeordneten Elements zu überschreiben.
Lassen Sie uns dies der Reihe nach überprüfen.
Memo
-Nicht implementiert ist "ein integrierter Typ für die Rückgabe, wenn eine Vergleichsoperation oder eine Binäroperation für einen nicht unterstützten Typ ausgeführt wird".
- __add__Gegen__radd__Eine solche Methode wird als Reflexionsmethode bezeichnet.
__radd__
aus, wenn __add__
nicht implementiert istA + B
Dieser Code wird in der folgenden Reihenfolge ausgeführt:
1. 「A.__add__Wird ausgeführt und "B" hinzugefügt.
2.Wenn "Nicht implementiert" in 1 zurückgegeben wird, wird "B".__radd__Hinzufügen von "A"
Wenn ** NotImplemented
** nicht zu den Zeitpunkten 1 bzw. 2 zurückgegeben wird, wird das hinzugefügte Ergebnis zurückgegeben und der Prozess endet. Wenn ** NotImplemented
** schließlich im Berechnungsergebnis zurückgegeben wird, wird ** TypeError
** zurückgegeben.
TypeError: unsupported operand type(s) for +: 'A' and 'B'
Lassen Sie uns dies mit Code überprüfen.
class A:
def __add__(self, other):
print('1. A.__add__ return NotImplemented')
return NotImplemented
class B:
def __radd__(self, other):
print('2. B.__radd__ return NotImplemented')
return NotImplemented
A() + B()
Wenn ich diesen Code ausführe, sieht er folgendermaßen aus:
Ausführungsergebnis
1. A.__add__ return NotImplemented
2. B.__radd__ return NotImplemented
Traceback (most recent call last):
File "oeprator_loading.py", line 32, in <module>
A() + B()
TypeError: unsupported operand type(s) for +: 'A' and 'B'
Anscheinend funktioniert es in der Reihenfolge, die ich oben geschrieben habe.
Aber wo wird die Ausnahme ** TypeError
** ausgelöst? Sowohl ** A .__ add__
** als auch ** B .__ radd__
** geben gerade ** NotImplemented
** zurück?
Suchen wir also nach dem Inhalt des CPython-Codes. Dann habe ich folgenden Code gefunden.
static PyObject *
binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
{
PyObject *result = binary_op1(v, w, op_slot);
if (result == Py_NotImplemented) {
Py_DECREF(result);
return binop_type_error(v, w, op_name);
}
return result;
}
// refs https://github.com/python/cpython/blob/3.6/Objects/abstract.c#L806
Es scheint, dass ** binop_type_error
** tatsächlich ** TypeError
** spuckt, und der Fluss ist wie folgt.
1. +Die Operatorverarbeitung wird ausgeführt
2. 「binary_In "op1"__add__ or __radd__Und schließlich erhalten Sie "Nicht implementiert"
3.Wenn Sie "Nicht implementiert" erhalten, wird "binop"_type_"Fehler" löst "TypeError" aus
Jetzt können Sie den Fluss sehen. Überprüfen Sie als Nächstes den Kommentarteil
__radd__
ausgeführtHinweis Der Typ des Operators auf der rechten Seite ist eine Unterklasse des Typs des Operators auf der linken Seite.
Wenn für diese Unterklassenmethode eine Reflexionsmethode definiert ist
Diese Methode wird aufgerufen, bevor die nicht reflektierende Methode des Operators auf der linken Seite aufgerufen wird.
Dieses Verhalten ermöglicht es Unterklassen, die Operation des übergeordneten Elements zu überschreiben.
Überprüfen Sie, ob es wirklich so funktioniert. Betrachten Sie den folgenden Code.
class A:
def __add__(self, other):
print('1. A.__add__ return 10')
return 10
class B:
def __radd__(self, other):
print('2. B.__radd__ return NotImplemented')
return NotImplemented
A() + B()
Ergebnis
1. A.__add__ return 10
In diesem Fall gibt ** A .__ add__
** nur ** return 10
** zurück, sodass ** B .__ radd__
** nicht ausgeführt wird. Lassen Sie uns nun die Klasse ** B
** von der Klasse ** A
** erben.
class A:
def __add__(self, other):
print('1. A.__add__ return 10')
return 10
class B(A): # <-Eine Klasse erben
def __radd__(self, other):
print('2. B.__radd__ return NotImplemented')
return NotImplemented
A() + B()
Ergebnis
2. B.__radd__ return NotImplemented
1. A.__add__ return 10
Dann wird zuerst ** B .__ radd__
** ausgeführt, und es wird bestätigt, dass" Wenn die rechte Seite eine Unterklasse auf der linken Seite des Operators ** ist, wird die Reflexionsmethode bevorzugt ausgeführt ** ". Es ist fertig.
Nur weil ** B .__ radd__
** bevorzugt ausgeführt wird, wird ** NotImplemented
** zurückgegeben, wenn ** NotImplemented
** zurückgegeben wird, auf ** A .__ add__
** zurückgreifen. verstanden.
Rufen Sie diese Methoden für die kumulative arithmetische Zuordnung auf(+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)Implementieren.
Diese Methoden führen die Operationen an Ort und Stelle aus(Verändere dich selbst)Versuche es,
als Ergebnis(Es muss nicht sein, aber es kann selbst sein)Muss zurückgegeben werden.
Wenn eine bestimmte Methode nicht definiert ist, greift ihre kumulative arithmetische Operation auf eine reguläre Methode zurück.
Zum Beispiel ist x__iadd__()Wenn es sich um eine Instanz einer Klasse handelt, die eine Methode hat, x+=y ist x= x.__iadd__(y)Ist äquivalent zu
Wenn nicht, x+x sowie y Bewertung.__add__(y)Andy.__radd__(x)Gilt als.
In bestimmten Situationen kann die kumulative Zuweisung zu unerwarteten Fehlern führen
(Warum wird es hinzugefügt a_tuple[i] += [‘item’]Wirft eine Ausnahme?Bitte beziehen Sie sich auf)Aber,
Dieses Verhalten ist tatsächlich Teil des Verhaltens des Datenmodells.
# refs http://docs.python.jp/3/reference/datamodel.html#object.__ior__
Sie können die Verarbeitung von "** Cumulative Arithmetic Substitution **" überladen. Mit anderen Worten, Sie können die Operation von "" A + = B`` "" anpassen.
NotImplemented
** zurückgibt
--Wenn das Operationsergebnis ** NotImplemented
** wird, TypeError: Nicht unterstützte Operandentypen für ..
werden gesendet.__ixxx__
) **, die nur ** kumulative arithmetische Zuordnung überladen kann **Ich habe noch einige andere Fragen, aber ich werde sie später irgendwo schreiben.
Ende
Recommended Posts