Heim  >  Artikel  >  Backend-Entwicklung  >  Eine kurze Diskussion zum Problem der Überlastung istInstanzvererbungsbeziehung in Python

Eine kurze Diskussion zum Problem der Überlastung istInstanzvererbungsbeziehung in Python

不言
不言Original
2018-05-04 14:21:241399Durchsuche

Dieser Artikel befasst sich hauptsächlich mit dem Problem der Überlastung der Vererbungsbeziehung. Jetzt können Freunde in Not auf die Vererbungsbeziehung zurückgreifen

Mit der integrierten Methode isinstance(object, classinfo) können Sie feststellen, ob ein Objekt eine Instanz einer bestimmten Klasse ist. Diese Beziehung kann direkt, indirekt oder abstrakt sein.

Instanzprüfungen dürfen überladen werden, siehe Dokument „customizing-instance-and-subclass-checks“. Gemäß der Beschreibung von PEP 3119:

Der hier vorgeschlagene primäre Mechanismus besteht darin, das Überladen der integrierten Funktionen isinstance() und issubclass() zu ermöglichen. Das Überladen funktioniert wie folgt: Der Aufruf isinstance(x , C) prüft zunächst, ob C.__instancecheck__ existiert, und ruft in diesem Fall C.__instancecheck__(x) anstelle seiner normalen Implementierung auf. , C) Bei der Erkennung wird zunächst geprüft, ob C.__instancecheck__ existiert .__instancecheck__(x) wird aufgerufen. Das zurückgegebene Ergebnis ist das Ergebnis der Instanzerkennung, und es gibt keine Standardbeurteilungsmethode.

Diese Methode hilft uns, die Enteneingabe zu überprüfen. Ich habe sie mit Code getestet.


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

Es wird nur False ausgegeben und __instancecheck__ wird nicht aufgerufen. Was ist los.

Es gibt keinen laufenden __instancecheck__

Es ist ersichtlich, dass das Dokument nicht klar geschrieben ist. Um das Problem herauszufinden, beginnen wir mit der Nachverfolgung der isinstance-Quellcode.

[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;
  ....
}

Py_TYPE(inst) == (PyTypeObject *)cls Dies ist eine schnelle Matching-Methode, äquivalent zu type(inst) ist cls , dies Wenn die Übereinstimmung auf diese schnelle Weise erfolgreich ist, wird __instancecheck__ nicht überprüft. Daher ist die Prioritätsprüfung im Dokument für die Existenz von C.__instancecheck__ falsch. Schauen Sie sich weiterhin den Quellcode an:

  /* We know what type's __instancecheck__ does. */
  if (PyType_CheckExact(cls)) {
    return recursive_isinstance(inst, cls);
  }

Erweitern Sie das Makro PyType_CheckExact:

[object.h]
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)

Das heißt, wenn cls eine Klasse ist, die direkt aus dem Typ erstellt wird, dann ist die Beurteilungssprache etabliert. Mit Ausnahme der in der Klassendeklaration angegebenen Metaklasse wird sie grundsätzlich direkt nach Typ erstellt. Wenn Sie anhand des Testcodes wissen, dass das Urteil gefällt wurde, geben Sie recursive_isinstance ein. Aber ich habe den Code zu __instancecheck__ in dieser Funktion nicht gefunden. Die Beurteilungslogik von recursive_isinstance lautet ungefähr:

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

wird anhand der __mro__-Vererbungsreihenfolge beurteilt. Gehen Sie zurück zur Funktion PyObject_IsInstance und schauen Sie nach unten:

if (PyTuple_Check(cls)) {
  ...
}

Dies ist der Fall, wenn der zweite Parameter von Instanz(x, C) ein Tupel ist und der Verarbeitungsmethode im Inneren Es handelt sich um einen rekursiven Aufruf von PyObject_IsInstance(inst, item). Weiterlesen:

checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
  res = PyObject_CallFunctionObjArgs(checker, inst, NULL);
  ok = PyObject_IsTrue(res);
  return ok;
}

Hier wird natürlich __instancecheck__ abgerufen, um den Prüfvorgang an diesen Punkt zu bringen, muss die definierte Klasse metaclass angeben. Jetzt müssen Sie nur noch _PyObject_LookupSpecial verfolgen:

[typeobject.c]
PyObject *
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
{
  PyObject *res;

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

nimmt Py_TYPE(self) an, was bedeutet, dass __instancecheck__ in der angegebenen Metaklasse definiert werden muss.

Zusammenfassung

An dieser Stelle fassen Sie die Bedingungen für das Überlastungsverhalten von isinstance(x, C) zusammen:

x-Objekte können nicht direkt von C instanziiert werden;

C-Klasse gibt Metaklasse an;
  1. __instancecheck__ ist in der angegebenen Metaklasse definiert.
  2. Testcode:
  3. 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

Die Dokumentation ist möglicherweise etwas alt. Die Umgebung für diesen Test ist Python3.6.

Verwandte Empfehlungen:

Detaillierte Erklärung von read_excel in Python 2.7 Pandas


Das obige ist der detaillierte Inhalt vonEine kurze Diskussion zum Problem der Überlastung istInstanzvererbungsbeziehung in Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn