2013-07-18 15 views
11

Es gibt diesen Code:Warum wird __instancecheck__ nicht immer abhängig vom Argument aufgerufen?

class Meta(type): 
    def __instancecheck__(self, instance): 
    print("__instancecheck__") 
    return True 

class A(metaclass=Meta): 
    pass 


a = A() 
isinstance(a, A) # __instancecheck__ not called 
isinstance([], A) # __instancecheck__ called 

Warum __instancecheck__ für [] Argument aufgerufen wird, aber nicht für a Argument?

+0

Dies ist verwandt mit (aber kein Duplikat): http://StackOverflow.com/Questions/13135712/Class-Method-Instancecheck-does-Not-Work – dnozay

Antwort

8

macht einen schnellen Test für die exakte Übereinstimmung.

Objects/abstract.c:

int 
PyObject_IsInstance(PyObject *inst, PyObject *cls) 
{ 
    static PyObject *name = NULL; 

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

Sie den schnellen Weg nicht gefallen hat? Sie können dies (auf eigene Gefahr) versuchen:

>>> import __builtin__ 
>>> def isinstance(a, b): 
...  class tmp(type(a)): 
...   pass 
...  return __builtin__.isinstance(tmp(), b) 
... 
>>> __builtin__.isinstance(a, A) 
True 
>>> isinstance(a, A) 
__instancecheck__ 
True 
+1

Angesichts, IMHO dieser Code hat einen Fehler. – martineau

4

Ich denke, dass der PEP beschreibt __instancecheck__() fehlerhaft ist. PEP 3119 sagt:

Der primäre Mechanismus hier vorgeschlagen wird, ist die integrierten Funktionen isinstance() und issubclass zu ermöglichen Überlastung(). Das Überladen funktioniert wie folgt: Der Aufruf isinstance (x, C) prüft zuerst, ob C.__instancecheck__ existiert, und wenn ja, ruft C.__instancecheck__(x) anstelle seiner normalen Implementierung.

Sie schreiben:

class C: 
    def do_stuff(self): 
     print('hello') 

C.do_stuff(C()) 

So basiert auf dem Zitat oben vom PEP, sollten Sie in der Lage sein

class C: 
    @classmethod 
    def __instancecheck__(cls, x): 
     print('hello') 


C.__instancecheck__(C()) 

--output:-- 
hello 

Aber isinstance() nicht nennen diese Methode zu schreiben:

class C: 
    @classmethod 
    def __instancecheck__(cls, y): 
     print('hello') 


x = C() 
isinstance(x, C) 

--output:-- 
<nothing> 

Die PEP geht dann zu sagen:

Diese Methoden bestimmt, deren metaclass auf Klassen aufgerufen werden, werden wird (abgeleitet von) ABCMeta ...

Okay, lassen wir das versuchen:

import abc 

class MyMeta(abc.ABCMeta): #A metaclass derived from ABCMeta 
    def __instancecheck__(cls, inst): 
     print('hello') 
     return True 

class C(metaclass=MyMeta): #A class whose metaclass is derived from ABCMeta 
    pass 


x = C() 
C.__instancecheck__(x) 

--output:-- 
hello 

Aber noch einmal isinstance() ruft diese Methode nicht auf:

isinstance(x, C) 

--output:-- 
<nothing> 

Fazit: PE P 3119 muss neu geschrieben werden - zusammen mit den "Datenmodell" -Dokumenten.

Verwandte Themen