Informationen zur Überlastungspriorität des Python-Operators

Überblick

―― Neulich hatte ich die Gelegenheit, auf der PyCon JP 2016 bekannt zu geben, also habe ich es angekündigt.

Frage

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

Antworten

A(1) + B(2) #=> A(3)

#=>Für eine Klasse__add__Ist implementiert, so funktioniert es zuerst

Was bedeutet das?

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.

1. Führen Sie __radd__ aus, wenn __add__ nicht implementiert ist

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

2. Wenn es sich um eine Unterklasse handelt, wird zuerst __radd__ ausgeführt

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.

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

3. Was ist iadd?

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.

Zusammenfassung

Ich habe noch einige andere Fragen, aber ich werde sie später irgendwo schreiben.

Ende

Referenz

Recommended Posts

Informationen zur Überlastungspriorität des Python-Operators
Python 3-Operator-Memo
Trinity-Operator (Python)
Python-Memo mit perl-ternärem Operator
Python oder und und Operatorfalle
String-Format mit Python% -Operator
Python
Python in ist auch ein Operator
[Python] Runden Sie nur mit dem Operator ab
Nicht logische Operatorverwendung von oder in Python