8

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

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

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

这是代码。请注意,鉴于带有实例的示例,将函数Bar“monkeypatch”到类(具有元类)中的选择可以正常工作。所以似乎不必直接在元类的类定义中定义函数。__subclasshook__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示例演示了猴子补丁的__subclasshook__工作方式。其他两个示例演示了__instancecheck__仅具有要检查的实例属性的实例的失败。在那些情况下,__instancecheck__甚至没有调用。

我可以手动检查(即,根据需要)或__instancecheck__的实例是否满足我的函数的条件。attrdicthasattr(instance_of_attrdict, "x")Truez

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

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

4

1 回答 1

2

一切都按原样工作。如果您正在使用__metaclass__,则您将覆盖类创建过程。看起来你的猴子补丁正在工作,__subclasshook__但它只是__subclasshook__ABCMeta. 你可以用这个检查它:

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

更明确地说:__subclasshook__在这个例子中,这种情况是偶然发生的,因为元类在某些情况下__subclasscheck__碰巧服从于类的。__subclasshook__元类的__instancecheck__协议从不遵循类的定义__instancecheck__,这就是为什么monkeypatched 版本__subclasshook__最终会被调用,但monkeypatched 版本__instancecheck__不会被调用的原因。

更详细地说:如果您正在使用元类创建一个类,则该类的类型将是元类。在这种情况下ABCMeta。并且定义isinstance()如下:'isinstance(object, class-or-type-or-tuple) -> bool',这意味着实例检查将在给定的类、类型或类/类型的元组上执行。在这种情况下,将在ABCMeta(ABCMeta.__instancecheck__()将被调用) 上进行 isinstance 检查。因为monkeypatch 应用于Foo类而不是ABCMeta,所以 的__instancecheck__方法Foo将永远不会运行。但是 的__instancecheck__ABCMethod调用__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__您正在寻找的那种协议,以便它确实遵循类的定义,__instancecheck__即元类的定义何时达到某些失败标准。然后,设置__metaclass__为内部的那个类,Foo并且您现有的装饰器应该按原样工作。

更多信息:关于元类的好帖子

于 2013-12-16T17:36:16.357 回答