__instancecheck__ metaclasses, how do they work: why do I get True when I tuple, why doesn't print run?

dieter dieter at handshake.de
Tue Nov 5 01:42:13 EST 2019


Veek M <veek at dont-use-this.com> writes:
I simplify your code to demonstrate what goes on:
>>> class MA(type):
...   def __instancecheck__(cls, inst):
...     print "MA", cls, inst
... 
>>> class AM(list): __metaclass__ = MA
... 
>>> am = AM()
>>> isinstance(am, AM)
True

As you can see, even with a class (rather than a tuple)
as second argument to "isinstance", "MA.__instancecheck__"
is not called.

I suppose that "isinstance" (at least under Python 2) does not
behave exactly as stated in PEP 3119. Instead, "isinstance"
first directly checks for the instance to be an instance of the
class *AND ONLY IF THIS FAILS* calls the class' "__instancecheck__".

As PEP 3119 suggests, "__instancecheck__" has been introduced
to support abstract base classes. The example given there
is an abstract base class "Sequence" and concrete derived classes
"list" and "tuple". For this use case, it is okay when
"isinstance(inst, cls)" returns "True" whenever "inst" is a "cls"
object and that "cls.__instancecheck__" is used only to result in
"True" also for other cases.


> 1. Why do I get True whenever i tuple the 
> isinstance(f, (Bar, Foo))
> (and why don't the print's run)
>
> The docs say that you can feed it a tuple and that the results are OR'd
>
> ----
> The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
> isinstance(x, A) or isinstance(x, B) or ... (etc.).
> -----
> which implies that the metaclasses are called for each class?
>
>
> class MyzMeta(type):
>     def __instancecheck__(cls, other):
>         print('MyzzzzzzMeta', other)
>         return 0
>
>
> class MyMeta(MyzMeta, object):
>     def __instancecheck__(cls, other):
>         print('MyMeta')
>         print(cls, other)
>         return 0
>
>         
> class Foo(list):
>     __metaclass__ = MyzMeta
>     pass
>
> class Miaow(object):
>     pass
>
> class Bar(Foo, Miaow):
>     __metaclass__ = MyMeta
>     pass
>
>
> f = Foo()
> b = Bar()
>
> print(isinstance(f, (Bar, Foo)))
> raise SystemExit
>
> if isinstance(f, (Bar, Foo)):
>     print('success')



More information about the Python-list mailing list