2013-07-18 3 views
11

Существует этот код:Почему __instancecheck__ не всегда вызывается в зависимости от аргумента?

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 

Почему __instancecheck__ вызывается для [] аргумента, но не для a аргумента?

+0

Это связано с (но не дубликат): http://stackoverflow.com/questions/13135712/class-method-instancecheck-does-not-work – dnozay

ответ

8

PyObject_IsInstance выполняет быструю проверку на точное соответствие.

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

не нравится быстрый путь? Вы можете попробовать это (на свой страх и риск):

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

Учитывая, что у IMHO этот код имеет ошибку. – martineau

4

Я думаю, что PEP описания __instancecheck__() неисправна. PEP 3119 говорит:

Основной механизм, предложенный здесь, чтобы перегружать встроенные функции isinstance() и issubclass(). Перегрузка работает следующим образом: вызов isinstance (x, C) сначала проверяет, существует ли C.__instancecheck__, и если да, то вызовы C.__instancecheck__(x) вместо обычной реализации.

Вы можете написать:

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

C.do_stuff(C()) 

Так на основе приведенной выше цитаты из PEP, вы должны быть в состоянии написать

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


C.__instancecheck__(C()) 

--output:-- 
hello 

Но isinstance() не вызывает этот метод:

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


x = C() 
isinstance(x, C) 

--output:-- 
<nothing> 

Затем PEP продолжает:

Эти методы предназначены для называться по классам, чьи метакласса является (на основе) ABCMeta ...

Хорошо, давайте попробуем, что:

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 

Но еще раз isinstance() не называет этот метод:

isinstance(x, C) 

--output:-- 
<nothing> 

Заключение: PE P 3119 необходимо переписать вместе с документами Data Model.

Смежные вопросы