Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't __instancecheck__ being called?

I have the following python3 code:

class BaseTypeClass(type):
    def __new__(cls, name, bases, namespace, **kwd):
        result = type.__new__(cls, name, bases, namespace)
        print("creating class '{}'".format(name))
        return result

    def __instancecheck__(self, other):
        print("doing instance check")
        print(self)
        print(other)
        return False


class A(metaclass=BaseTypeClass):
    pass

print(type(A))
print(isinstance(A(), A))

and when I run it on Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32 I get the following output

creating class 'A'
<class '__main__.BaseTypeClass'>
True

Why isn't it outputting doing instance check? The documentation says the __instancecheck__ method needs to be defined on the metaclass and not the class itself, which I have done here. I even verify the metaclass is being used since creating class 'A' is printed. However, when I call isinstance it appears to be using the default implementation and not the one I defined in the metaclass.

I'm probably not using metaclasses correctly, but I can't figure out where I made my mistake.

like image 886
drdrez Avatar asked Dec 10 '17 16:12

drdrez


1 Answers

The isinstance function makes a quick check to see if the type of the instance supplied as an argument is the same as that of the class. If so, it returns early and doesn't invoke your custom __instancecheck__.

This is an optimization used in order to avoid an expensive call to __instancecheck__ (it's Pythonland code) when it isn't required.

You can see the specific test in PyObject_IsInstance, the function that handles the isinstance call in the CPython implementation:

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

Of course, your __instancecheck__ fires correctly when that test isn't True:

>>> isinstance(2, A)
doing instance check
<class '__main__.A'>
2
False

I am not certain if this is implementation specific, I would of thought so, though, since there's no reference to this in the corresponding PEP section nor in the documentation on isinstance.


Interesting aside: issubclass actually doesn't behave this way. Due to its implementation it always calls __subclasscheck__. I had opened an issue on this a while back which is still pending.

like image 137
Dimitris Fasarakis Hilliard Avatar answered Oct 19 '22 06:10

Dimitris Fasarakis Hilliard