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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With