Why is __instancecheck__ not 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 win32I get the following output

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

Why is not displayed doing instance check? The documentation says that the method __instancecheck__should be defined in the metaclass, and not in the class itself, which I did here. I even check that the metaclass is being used since creating class 'A'. However, when I call isinstanceit seems that it uses the default implementation, and not the one I defined in the metaclass.

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

+5
source share
2 answers

isinstance , , , , , . , __instancecheck__.

, __instancecheck__ ( Pythonland), .

PyObject_IsInstance, isinstance CPython:

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

, __instancecheck__ , True:

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

, , , ) a > isinstance.


: issubclass . __subclasscheck__. , .

+5

, , .

, - , instancheck (, , , , , , , ), , .

, " ", . , "instancheck" fail, .

def sub__new__(cls, *args, **kw):
    metacls = cls.__class__
    new_cls = metacls(cls.__name__, cls.__bases__, dict(cls.__dict__), clonning=cls)
    return new_cls(*args, **kw)

class M(type):
    shadows = {}
    rev_shadows = {}
    def __new__(metacls, name, bases, namespace, **kwd):
        clonning = kwd.pop("clonning", None)
        if not clonning:
            cls = super().__new__(metacls, name, bases, namespace)
            # Assumes classes don't have  a `__new__` of them own.
            # if they do, it is needed to wrap it.
            cls.__new__ = sub__new__
        else:
            cls = clonning
            if cls not in metacls.shadows:
                clone = super().__new__(metacls, name, bases, namespace)
                # The same - replace for unwrapped new.
                del clone.__new__
                metacls.shadows[cls] = clone
                metacls.rev_shadows[clone] = cls
            return metacls.shadows[cls]

        return cls

    def __setattr__(cls, attr, value):

        # Keep class attributes in sync with shadoclass
        # This could be done with 'super', but we'd need a thread lock
        # and check for re-entering.
        type.__setattr__(cls, attr, value)
        metacls = type(cls)
        if cls in metacls.shadows:
            type.__setattr__(metacls.shadows[cls], attr, value)
        elif cls in metacls.rev_shadows:
            type.__setattr__(metacls.rev_shadows[cls], attr, value)    

    def call(cls, *args, **kw):
        # When __new__ don't return an instance of its class,
        # __init__ is not called by type __call__
        instance = cls.__new__(*args, **kw)
        instance.__init__(*args, **kw)
        return instance

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


class A(metaclass=M):
    pass

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

. , , - , , , __new__. __new__ super, , super .

+3

Source: https://habr.com/ru/post/1690517/


All Articles