2013-07-18 14 views
11

C'è questo codice:Perché __instancecheck__ non viene sempre chiamato a seconda dell'argomento?

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 

Perché __instancecheck__ è chiamato per [] argomento, ma non per a argomento?

+0

questo è correlato a (ma non a un duplicato): http://stackoverflow.com/questions/13135712/class-method-instancecheck-does-not-work – dnozay

risposta

8

PyObject_IsInstance esegue un test rapido per la corrispondenza esatta.

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; 
// ... 

non piace il percorso veloce? si può provare questo (a vostro rischio):

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

Dato che, IMHO questo codice ha un bug. – martineau

4

Penso che il PEP descrive __instancecheck__() è difettoso. PEP 3119 afferma:

Il meccanismo principale qui proposto è quello di consentire di sovraccaricare il funzioni integrate isinstance() e issubclass(). L'overloading funziona come segue: La chiamata isinstance (x, C) controlla innanzitutto se esiste C.__instancecheck__ e, in tal caso, chiama C.__instancecheck__(x) invece della sua normale implementazione.

È possibile scrivere:

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

C.do_stuff(C()) 

Quindi, sulla base della citazione di cui sopra dal PEP, si dovrebbe essere in grado di scrivere

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


C.__instancecheck__(C()) 

--output:-- 
hello 

Ma isinstance() non chiama questo metodo:

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


x = C() 
isinstance(x, C) 

--output:-- 
<nothing> 

Il PEP prosegue dicendo:

Questi metodi sono destinati ad essere chiamato in classi i cui metaclasse è (derivato da) ABCMeta ...

Va bene, proviamo che:

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 

Ma ancora una volta isinstance() non chiama questo metodo:

isinstance(x, C) 

--output:-- 
<nothing> 

Conclusione: PE P 3119 deve essere riscritto - insieme ai documenti "Modello dati".

Problemi correlati