是否可以使用 Python 元类反向继承?

dan*_*jar 5 python inheritance metaprogramming metaclass python-3.x

出于好奇,我想知道是否可以编写一个元类,使父类的方法优先于子类的方法。我想玩一会儿。不可能再覆盖方法了。基类必须显式调用子方法,例如使用对基实例的引用。

class Base(metaclass=InvertedInheritance):

    def apply(self, param):
        print('Validate parameter')
        result = self.subclass.apply(param)
        print('Validate result')
        return result


class Child(Base):

    def apply(self, param):
        print('Compute')
        result = 42 * param
        return result


child = Child()
child.apply(2)
Run Code Online (Sandbox Code Playgroud)

随着输出:

验证参数
计算
验证结果

Bre*_*arn 4

如果您只关心以相反的顺序查找实例(而不是类),那么您甚至不需要元类。你可以覆盖__getattribute__

class ReverseLookup:
    def __getattribute__(self, attr):
        if attr.startswith('__'):
            return super().__getattribute__(attr)
        cls = self.__class__
        if attr in self.__dict__:
            return self.__dict__[attr]
        # Using [-3::-1] skips topmost two base classes, which will be ReverseLookup and object
        for base in cls.__mro__[-3::-1]:
            if attr in base.__dict__:
                value = base.__dict__[attr]
                # handle descriptors
                if hasattr(value, '__get__'):
                    return value.__get__(self, cls)
                else:
                    return value
        raise AttributeError("Attribute {} not found".format(attr))

class Base(ReverseLookup):
    def apply(self, param):
        print('Validate parameter')
        result = self.__class__.apply(self, param)
        print('Validate result')
        return result

class Child(Base):
    def apply(self, param):
        print('Compute')
        result = 42 * param
        return result

>>> Child().apply(2)
Validate parameter
Compute
Validate result
84
Run Code Online (Sandbox Code Playgroud)

这种机制相对简单,因为类的查找不是反向的:

>>> Child.apply
<function Child.apply at 0x0000000002E06048>
Run Code Online (Sandbox Code Playgroud)

这使得只需在类而不是实例上进行“正常”查找就变得很容易。但是,在其他情况下,这可能会导致混乱,例如基类方法尝试访问子类上的不同方法,但该方法实际上不存在于该子类上;在这种情况下,查找将按正常方向进行,并可能找到更高类上的方法。换句话说,执行此操作时,您必须确保不会在类上查找任何方法,除非您确定它们是在该特定类上定义的。

很可能在其他极端情况下这种方法不起作用。特别是你可以看到我临时操纵了描述符处理;__set__如果它对带有 a 的描述符做了一些奇怪的事情,或者对于更频繁地使用传递给 的类/对象参数的更复杂的描述符,我不会感到惊讶__get__。此外,此实现会依赖于以两个下划线开头的任何属性的默认行为;__init__改变这一点需要仔细考虑它将如何与像和一样的魔术方法一起工作__repr__