gpt4 book ai didi

python - 为什么 `__subclasshook__` 可以被猴子修补到元类上,而 `__instancecheck__` 不能?

转载 作者:太空狗 更新时间:2023-10-29 21:09:58 30 4
gpt4 key购买 nike

这是一个尝试创建装饰器的玩具示例,该装饰器允许声明属性名称,这些名称应该是标准 __subclasshook____instancecheck__ 中“接口(interface)检查”的必需部分模式。

当我装饰 Foo 类时,它似乎按预期工作。我制作了一个 Bar 类,与 Foo 无关,但它具有所需的属性,并且它正确地满足 isinstance(instance_of_bar, Foo) == True.

但作为另一个例子,我扩展了 dict 的子类,这样 key 值也可以通过 getattr 语法访问(例如 dict,其中 d['a'] 可以替换为 d.a 以获得相同的结果)。在这种情况下,属性只是实例属性,所以 __instancecheck__ 应该可以工作。

这是代码。请注意,鉴于带有 Bar 实例的示例有效,选择将 __subclasshook__ 函数“monkeypatch”到 Foo 类(具有一个元类)工作正常。所以看起来必须直接在元类的类定义中定义函数。

#Using Python 2.7.3

import abc
def interface(*attributes):
def decorator(Base):

def checker(Other):
return all(hasattr(Other, a) for a in attributes)

def __subclasshook__(cls, Other):
if checker(Other):
return True
return NotImplemented

def __instancecheck__(cls, Other):
return checker(Other)

Base.__subclasshook__ = classmethod(__subclasshook__)
Base.__instancecheck__ = classmethod(__instancecheck__)
return Base

return decorator

@interface("x", "y")
class Foo(object):
__metaclass__ = abc.ABCMeta
def x(self): return 5
def y(self): return 10

class Bar(object):
def x(self): return "blah"
def y(self): return "blah"

class Baz(object):
def __init__(self):
self.x = "blah"
self.y = "blah"

class attrdict(dict):
def __getattr__(self, attr):
return self[attr]

f = Foo()
b = Bar()
z = Baz()
t = attrdict({"x":27.5, "y":37.5})

print isinstance(f, Foo)
print isinstance(b, Foo)
print isinstance(z, Foo)
print isinstance(t, Foo)

打印:

True
True
False
False

这只是一个玩具示例——我并不是在寻找更好的方法来实现我的 attrdict 类。 Bar 示例演示了 monkeypatched __subclasshook__ 的工作。其他两个示例演示了 __instancecheck__ 对于仅具有要检查的实例属性的实例的失败。在这些情况下,甚至不会调用 __instancecheck__

我可以手动检查 __instancecheck__ 函数中的条件是否被 attrdict 的实例满足(即 hasattr(instance_of_attrdict, "x") 根据需要为 True)或 z

同样,对于 Bar 的实例,它似乎工作正常。这表明 __subclasshook__ 被装饰器正确应用,并且修补自定义 __metaclass__ 不是问题。但是__instancecheck__好像没有在这个过程中被调用。

为什么 __subclasshook__ 可以在元类的类定义之外定义并在以后添加,而 __instancecheck__ 不能?

最佳答案

一切正常。如果您使用的是 __metaclass__,那么您将覆盖类创建过程。看起来您的 monkeypatch 正在为 __subclasshook__ 工作,但它仅从 ABCMeta__subclasshook__ 函数调用。你可以用这个检查它:

>>> type(Foo)
<class 'abc.ABCMeta'>

更明确地说:__subclasshook__ 案例在这个例子中是偶然的,因为元类的 __subclasscheck__ 恰好服从类的 __subclasshook__在某些情况下。元类的 __instancecheck__ 协议(protocol)永远不会遵从类的 __instancecheck__ 定义,这就是 __subclasshook__ 的 monkeypatched 版本最终会被调用的原因,但是 __instancecheck__ 的 monkeypatched 版本不会被调用。

详细信息:如果您使用元类创建类,则该类的类型将是元类。在本例中是 ABCMetaisinstance() 的定义如下:'isinstance(object, class-or-type-or-tuple) -> bool',表示实例检查将在给定的类、类型或类/类型的元组上执行。在这种情况下,isinstance 检查将在 ABCMeta 上完成(将调用 ABCMeta.__instancecheck__())。因为 monkeypatch 应用于 Foo 类而不是 ABCMetaFoo__instancecheck__ 方法将永远不会运行.但是 ABCMethod__instancecheck__ 会调用它自己的 __subclasscheck__ 方法,而这第二个方法会通过执行 __subclasshook__ 来尝试验证 创建类的方法(在本例中为 Foo)。

在这种情况下,如果您像这样覆盖元类的函数,您可以获得所需的行为:

def interface(*attributes):
def decorator(Base):

def checker(Other):
return all(hasattr(Other, a) for a in attributes)

def __subclasshook__(cls, Other):
if checker(Other):
return True
return NotImplemented

def __instancecheck__(cls, Other):
return checker(Other)

Base.__metaclass__.__subclasshook__ = classmethod(__subclasshook__)
Base.__metaclass__.__instancecheck__ = classmethod(__instancecheck__)
return Base

return decorator

更新后的代码输出:

True
True
True
True

另一种方法是定义您自己的类作为元类,并创建您正在寻找的那种 __instancecheck__ 协议(protocol),这样它就会当元类的定义达到某些失败标准时,遵从类的 __instancecheck__ 定义。然后,将 __metaclass__ 设置为 Foo 中的那个类,您现有的装饰器应该按原样工作。

更多信息:A good post about metaclasses

关于python - 为什么 `__subclasshook__` 可以被猴子修补到元类上,而 `__instancecheck__` 不能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20528865/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com