Maison >développement back-end >Tutoriel Python >Une brève discussion sur le problème de la surcharge de la relation d'héritage isinstance en Python

Une brève discussion sur le problème de la surcharge de la relation d'héritage isinstance en Python

不言
不言original
2018-05-04 14:21:241424parcourir

Cet article présente principalement le problème de la surcharge de la relation d'héritage isinstance en Python. Il a une certaine valeur de référence. Maintenant, je le partage avec vous. Les amis dans le besoin peuvent se référer à

pour le jugement de la relation d'héritage.

À l'aide de la méthode intégrée isinstance(object, classinfo), vous pouvez déterminer si un objet est une instance d'une certaine classe. Cette relation peut être directe, indirecte ou abstraite.

Les vérifications d'instance peuvent être surchargées, voir le document personnalisation des vérifications d'instance et de sous-classe. D'après la description du PEP 3119 :

Le mécanisme principal proposé ici est de permettre la surcharge des fonctions intégrées isinstance() et issubclass() : L'appel isinstance(x. , C ) vérifie d'abord si C.__instancecheck__ existe, et si c'est le cas, appelle C.__instancecheck__(x) au lieu de son implémentation normale, C) Lors de la détection, il vérifiera d'abord si C.__instancecheck__ existe. .__instancecheck__(x) sera appelé. Le résultat renvoyé est le résultat de la détection d'instance et il n'y a pas de méthode de jugement par défaut.

Cette méthode nous aide à vérifier la frappe du canard, je l'ai testée avec du code.

Seul False est imprimé et __instancecheck__ n'est pas appelé. Que se passe-t-il.
class Sizeable(object):
  def __instancecheck__(cls, instance):
    print("__instancecheck__ call")
    return hasattr(instance, "__len__")

class B(object):
  pass

b = B()
print(isinstance(b, Sizeable)) # output:False

Il n'y a pas de __instancecheck__ en cours d'exécution

On peut voir que le document n'est pas clairement écrit Afin de découvrir le problème, nous commençons le traçage à partir de. le code source d'isinstance.

Py_TYPE(inst) == (PyTypeObject *)cls Il s'agit d'une méthode de correspondance rapide, équivalente à type(inst) is cls , si le la correspondance est réussie de cette manière rapide, __instancecheck__ ne sera pas vérifié. Ainsi, la vérification prioritaire dans le document de l'existence de C.__instancecheck__ est erronée. Continuez à regarder le code source :
[abstract.c]
int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
  _Py_IDENTIFIER(__instancecheck__);
  PyObject *checker;

  /* Quick test for an exact match */
  if (Py_TYPE(inst) == (PyTypeObject *)cls)
    return 1;
  ....
}

Développez la macro PyType_CheckExact :
  /* We know what type's __instancecheck__ does. */
  if (PyType_CheckExact(cls)) {
    return recursive_isinstance(inst, cls);
  }

C'est-à-dire que cls est une classe directement construite à partir du type, puis le langage de jugement est établi. À l'exception de la métaclasse spécifiée dans la déclaration de classe, elle est essentiellement construite directement par type. Sachant grâce au code de test que le jugement est établi, entrez recursive_isinstance. Mais je n'ai trouvé aucun code sur __instancecheck__ dans cette fonction. La logique de jugement de recursive_isinstance est grossièrement :
[object.h]
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)

est jugée à partir de l'ordre d'héritage de. __mro__ de. Revenez à la fonction PyObject_IsInstance et regardez vers le bas :
def recursive_isinstance(inst, cls):
  return pyType_IsSubtype(inst, cls)

def pyType_IsSubtype(a, b):
  for mro in a.__mro__:
    if mro is b:
      return True
  return False

C'est à ce moment-là que le deuxième paramètre de instance(x, C) est un tuple, à l'intérieur de The La méthode de traitement consiste à appeler PyObject_IsInstance(inst, item) de manière récursive. Continuer la lecture :
if (PyTuple_Check(cls)) {
  ...
}

Évidemment, c'est là que __instancecheck__ est obtenu. Afin d'amener le processus de vérification à ce point, la classe définie doit spécifier la métaclasse. . Il ne reste plus qu'à suivre _PyObject_LookupSpecial :
checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
  res = PyObject_CallFunctionObjArgs(checker, inst, NULL);
  ok = PyObject_IsTrue(res);
  return ok;
}

prend Py_TYPE(self), ce qui signifie que la métaclasse spécifiée doit être définie_ _instancecheck__ .
[typeobject.c]
PyObject *
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
{
  PyObject *res;

  res = _PyType_LookupId(Py_TYPE(self), attrid);
  // 有回调的话处理回调
  // ...
  return res;
}

Résumé

À ce stade, résumez les conditions de surcharge du comportement isinstance(x, C) :

x les objets ne peuvent pas être directement instanciés par C ;

  1. La classe C spécifie la métaclasse

  2. __instancecheck__ est définie dans la métaclasse spécifiée ;

  3. Code de test :

La documentation est peut-être un peu ancienne. L'environnement de ce test est Python3.6.
class MetaSizeable(type):
  def __instancecheck__(cls, instance):
    print("__instancecheck__ call")
    return hasattr(instance, "__len__")

class Sizeable(metaclass=MetaSizeable):
  pass

class B(object):
  pass

b = B()
print(isinstance(b, Sizeable)) # output: False
print(isinstance([], Sizeable)) # output: True

Recommandations associées :


Explication détaillée de read_excel dans Python 2.7 pandas

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn