―― L'autre jour, j'ai eu l'occasion de l'annoncer à PyCon JP 2016, alors je l'ai annoncé.
-Quelle méthode a la priorité sur l'objet A qui définit ** __ add__
** ou sur l'objet B qui définit ** __radd__
**?
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) #=>D'un__add__Est-ce que ça marche? B's__radd__Est-ce que ça marche?
--Le ** ʻA .__ add__` ** sur la gauche a la priorité et est exécuté.
__radd__
** semble être une méthode de secours lorsque ** __ add__
** n'est pas implémenté.
«Je n'ai pas bien compris ce domaine, alors j'ai donné une mauvaise explication. Excusez-moi.A(1) + B(2) #=> A(3)
#=>Pour une classe__add__Est implémenté, donc ça marche en premier
Documentation a l'explication suivante dans l'explication des méthodes système ** __ rxxx__
**. ..
En appelant ces méthodes, l'opérateur de l'opération arithmétique binomiale était reflété(Fut remplacé)Mettez en œuvre des choses.
Ces fonctions ne sont appelées que si l'opérateur on à gauche ne prend pas en charge l'opération correspondante et les non-opérateurs sont de types différents.
Par exemple, y est__rsub__()S'il s'agit d'une instance d'une classe avec une méthode
Expression x-Lorsque y est évalué, x.__sub__(y)Y lorsque renvoie NotImplemented.__rsub__(x)Est appelé.
# refs http://docs.python.jp/3/reference/datamodel.html#object.__ror__
Il est également écrit comme une note comme suit.
Remarque Le type de l'opérateur à droite est une sous-classe du type de l'opérateur à gauche,
Si une méthode de réflexion est définie pour cette méthode de sous-classe
Cette méthode est appelée avant que la méthode non réfléchissante de l'opérateur de gauche ne soit appelée.
Ce comportement permet aux sous-classes de remplacer l'opération du parent.
Vérifions cela dans l'ordre.
Note
-Non implémenté est "un type intégré à renvoyer lorsqu'une opération de comparaison ou une opération binaire est effectuée sur un type non pris en charge"
- __add__Contre__radd__Une méthode comme celle-ci est appelée une méthode de réflexion.
A + B
Ce code s'exécute dans l'ordre suivant:
1. 「A.__add__Est exécuté et "B" est ajouté.
2.Si "Non mis en œuvre" est renvoyé en 1, "B".__radd__Pour ajouter "A"
Si ** NotImplemented
** n'est pas retourné aux horaires 1 et 2, respectivement, le résultat ajouté est retourné et le processus se termine. Lorsque ** NotImplemented
** est finalement renvoyé dans le résultat du calcul, ** TypeError
** est renvoyé.
TypeError: unsupported operand type(s) for +: 'A' and 'B'
Vérifions cela avec du code.
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()
Quand j'exécute ce code, cela ressemble à ceci:
Résultat d'exécution
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'
Apparemment, cela fonctionne dans l'ordre que j'ai écrit ci-dessus.
Mais où l'exception ** TypeError
** est-elle levée? ** ʻA .__ add__** et **
B .__ radd__ ** retournent juste **
NotImplemented` **?
Cherchons donc le contenu du code CPython. Ensuite, j'ai trouvé le code suivant.
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
Il semble que ** binop_type_error
** crache réellement ** TypeError
**, et le flux est le suivant.
1. +Le traitement de l'opérateur est exécuté
2. 「binary_Dans "op1"__add__ or __radd__Et enfin recevoir "Non mis en œuvre"
3.Lorsque vous recevez «Non mis en œuvre», «binop»_type_"Error" lance "TypeError"
Vous pouvez maintenant voir le flux. Ensuite, vérifiez la partie commentaire
__radd__
sera exécuté en premierRemarque Le type de l'opérateur à droite est une sous-classe du type de l'opérateur à gauche,
Si une méthode de réflexion est définie pour cette méthode de sous-classe
Cette méthode est appelée avant que la méthode non réfléchissante de l'opérateur de gauche ne soit appelée.
Ce comportement permet aux sous-classes de remplacer l'opération du parent.
Vérifiez si cela fonctionne vraiment comme ça. Considérez le code ci-dessous.
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()
résultat
1. A.__add__ return 10
Dans ce cas ** ʻA .__ add ** renvoie uniquement **
return 10 **, donc **
B . radd __** ne sera pas exécuté. Maintenant héritons de la classe **
B ** de la classe ** ʻA
**.
class A:
def __add__(self, other):
print('1. A.__add__ return 10')
return 10
class B(A): # <-Hériter d'une classe
def __radd__(self, other):
print('2. B.__radd__ return NotImplemented')
return NotImplemented
A() + B()
résultat
2. B.__radd__ return NotImplemented
1. A.__add__ return 10
Ensuite, ** B .__ radd__
** est exécuté en premier, et il est confirmé que" Si le côté droit est une sous-classe du côté gauche de l'opérateur **, la méthode de réflexion est préférentiellement exécutée ** ". C'est fait.
Aussi, juste parce que ** B .__ radd__
** est exécuté préférentiellement, si ** NotImplemented
** est renvoyé, il reviendra à ** ʻA .__ add__` **. compris.
Appelez ces méthodes pour l'affectation arithmétique cumulative(+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)Implémenter.
Ces méthodes font les opérations en place(Changer de soi)Essaye de faire,
Par conséquent(Ça ne doit pas être, mais ça peut être soi)Doit être retourné.
Si une méthode particulière n'est pas définie, son opération arithmétique cumulative reviendra à une méthode régulière.
Par exemple, x est__iadd__()S'il s'agit d'une instance d'une classe qui a des méthodes, x+=y est x= x.__iadd__(y)Est équivalent à
Sinon, x+cote x et y.__add__(y)Andy.__radd__(x)Seront considérées.
Dans certaines situations, l'affectation cumulative peut entraîner des erreurs inattendues
(Pourquoi est-il ajouté un_tuple[i] += [‘item’]Lève une exception?Prière de se référer à)Mais,
Ce comportement fait en fait partie du comportement du modèle de données.
# refs http://docs.python.jp/3/reference/datamodel.html#object.__ior__
Vous pouvez surcharger le traitement de "** Substitution arithmétique cumulative **". En d'autres termes, vous pouvez personnaliser le fonctionnement de "` ʻA + = B`` ".
--Operator Overload est exécuté dans l'ordre de "** 1. Méthode de l'opérateur-> 2. Méthode de réflexion **"
NotImplemented
**
--Lorsque le résultat de l'opération devient ** NotImplemented
**, TypeError: type (s) d'opérande non pris en charge pour ..
est envoyé.__ixxx__
) ** qui ne peut que surcharger ** l'affectation arithmétique cumulative **J'ai d'autres questions, mais je les écrirai quelque part plus tard.
Fin
Recommended Posts