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

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

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)

随着输出:

Validate parameter
Compute
Validate result

解决方法:

如果只关心使实例以相反的顺序(而不是类)进行查找,则甚至不需要元类.您可以覆盖__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

这种机制相对简单,因为在类上的查找并非相反:

>>> Child.apply
<function Child.apply at 0x0000000002E06048>

这使得仅在类而不是实例上进行“常规”查找变得容易.但是,在其他情况下,这可能会造成混乱,例如,如果基类方法试图访问子类上的其他方法,但该方法实际上在该子类上不存在;在这种情况下,查找将沿正常方向进行,并可能在更高级别上找到该方法.换句话说,执行此操作时,请确保不要在类上查找任何方法,除非您确定它们是在该特定类上定义的.

在其他极端情况下,这种方法可能行不通.特别是,您可以看到我陪审了描述符处理;如果它对带有__set__的描述符或对更复杂的描述符进行了更奇怪的处理,而这些描述符更密集地使用了传递给__get__的类/对象,我不会感到惊讶.同样,对于任何以两个下划线开头的属性,此实现都依赖默认行为.要更改此设置,需要仔细考虑如何与__init__和__repr__之类的魔术方法一起使用.

上一篇:Ulysses下载,Markdown文本编辑器


下一篇:C++核心准则T.120:只在确实有需要时使用模板元编程