为什么 __instancecheck__ 没有被调用?
Why isn't __instancecheck__ being called?
我有以下 python3 代码:
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))
当我 运行 它在 Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
上时,我得到以下输出
creating class 'A'
<class '__main__.BaseTypeClass'>
True
为什么不输出doing instance check
? documentation 表示 __instancecheck__
方法需要在 metaclass 上定义,而不是 class 本身,我已经在这里完成了。我什至验证了 metaclass 被使用,因为 creating class 'A'
被打印出来了。但是,当我调用 isinstance
时,它似乎使用的是默认实现,而不是我在 metaclass.
中定义的实现
我可能没有正确使用 metaclasses,但我不知道我在哪里犯了错误。
isinstance
函数快速检查作为参数提供的实例类型是否与 class 的相同。如果是这样,它会提前 returns 并且不会调用您的自定义 __instancecheck__
。
这是一项优化,用于避免在不需要时对 __instancecheck__
(它是 Pythonland 代码)的昂贵调用。
你可以看到specific test in PyObject_IsInstance
,在CPython实现中处理isinstance
调用的函数:
/* Quick test for an exact match */
if (Py_TYPE(inst) == (PyTypeObject *)cls)
return 1;
当然,当该测试不是 True
:
时,您的 __instancecheck__
会正确触发
>>> isinstance(2, A)
doing instance check
<class '__main__.A'>
2
False
我不确定这是否是特定于实现的,不过我认为是这样,因为在 the corresponding PEP section 和 isinstance
.[=25= 的文档中都没有提到这一点]
有趣的是:issubclass
实际上并不是这样的。由于它的实现,它总是调用 __subclasscheck__
。不久前,我已经打开了 an issue,但仍处于待定状态。
Jim 的回答似乎一语中的。
但是对于那些需要出于某种奇怪原因完全定制的人
instancheck(好的,现在我正在写这个,似乎有
没有正确的理由想要那个,但愿我是错的),一个 metaclass 可以得到
远离它,但这很棘手。
这个动态替换实际的 class
由 "shadow class" 实例化的对象,即
是原件的克隆。这样,原生 "instancheck" 总是
失败,metaclass 被调用。
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's __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))
它甚至有一种机制可以在阴影 class 和实际 class 中同步属性。它不支持的一件事是如果以这种方式处理的 classes 确实实现了自定义 __new__
。如果这样的 __new__
使用无参数 super
,它开始变得棘手,因为 super 的参数不会是影子 class。
我有以下 python3 代码:
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))
当我 运行 它在 Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
上时,我得到以下输出
creating class 'A'
<class '__main__.BaseTypeClass'>
True
为什么不输出doing instance check
? documentation 表示 __instancecheck__
方法需要在 metaclass 上定义,而不是 class 本身,我已经在这里完成了。我什至验证了 metaclass 被使用,因为 creating class 'A'
被打印出来了。但是,当我调用 isinstance
时,它似乎使用的是默认实现,而不是我在 metaclass.
我可能没有正确使用 metaclasses,但我不知道我在哪里犯了错误。
isinstance
函数快速检查作为参数提供的实例类型是否与 class 的相同。如果是这样,它会提前 returns 并且不会调用您的自定义 __instancecheck__
。
这是一项优化,用于避免在不需要时对 __instancecheck__
(它是 Pythonland 代码)的昂贵调用。
你可以看到specific test in PyObject_IsInstance
,在CPython实现中处理isinstance
调用的函数:
/* Quick test for an exact match */
if (Py_TYPE(inst) == (PyTypeObject *)cls)
return 1;
当然,当该测试不是 True
:
__instancecheck__
会正确触发
>>> isinstance(2, A)
doing instance check
<class '__main__.A'>
2
False
我不确定这是否是特定于实现的,不过我认为是这样,因为在 the corresponding PEP section 和 isinstance
.[=25= 的文档中都没有提到这一点]
有趣的是:issubclass
实际上并不是这样的。由于它的实现,它总是调用 __subclasscheck__
。不久前,我已经打开了 an issue,但仍处于待定状态。
Jim 的回答似乎一语中的。
但是对于那些需要出于某种奇怪原因完全定制的人 instancheck(好的,现在我正在写这个,似乎有 没有正确的理由想要那个,但愿我是错的),一个 metaclass 可以得到 远离它,但这很棘手。
这个动态替换实际的 class 由 "shadow class" 实例化的对象,即 是原件的克隆。这样,原生 "instancheck" 总是 失败,metaclass 被调用。
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's __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))
它甚至有一种机制可以在阴影 class 和实际 class 中同步属性。它不支持的一件事是如果以这种方式处理的 classes 确实实现了自定义 __new__
。如果这样的 __new__
使用无参数 super
,它开始变得棘手,因为 super 的参数不会是影子 class。