【问题标题】:Is it possible to inverse inheritance using a Python meta class?是否可以使用 Python 元类进行反向继承?
【发布时间】:2016-11-19 04:46:10
【问题描述】:

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

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)

输出:

验证参数
计算
验证结果

【问题讨论】:

  • 您已标记此 Python 3,但您使用的是 Python 2 元类语法,Python 3 完全忽略了该语法。如果你想在 Python 3 上指定一个元类,你应该做class Foo(metaclass=Whatever)
  • @user2357112 谢谢,已更正。
  • self.subclass 意味着什么?如果Child.apply 旨在返回Base.apply,而child.subclassChild,那么self.subclass.apply 将与Child.apply 相同并且仍将返回Base.apply。属性访问机制不会“知道”您是尝试从外部代码还是从继承反转机制内部获取属性。您希望这种反向查找在直接访问类上的属性时起作用,还是仅在通过实例访问它们时起作用?
  • @BrenBarn 是的,我明白了。这是一种访问子方法的方式。它可以是一个辅助对象,包含对子方法的引用。
  • 这听起来像亚麻github.com/google/flax - 我搜索的东西完全一样,就因为亚麻。

标签: python python-3.x inheritance metaprogramming metaclass


【解决方案1】:

如果您只关心实例上的查找以相反的顺序(而不是类),那么您甚至不需要元类。你可以直接覆盖__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__ 等魔术方法。

【讨论】:

  • 谢谢。如果有A&lt;-B&lt;-C的继承链,self.__class__.apply()中的A会找到B的方法还是离开类C?此外,在继承菱形A&lt;-B,C&lt;-D 中,我想以相反的顺序使用协作继承,以便在任何地方调用self.sub.apply() 都转到A, B, C, D
  • @danijar: 在 C 的 instance 上调用 apply 将调用 A 上的方法,然后 A 中的 self.__class__.apply() 将在 C 上调用它。如果你真的想要处理各种继承树,我建议你完全忽略 Python 现有的继承机制并自己实现整个事情(例如,使用名为 ReverseParent 的类属性或其他任何东西,而不是实际从“父”继承)。但老实说,我不明白这一点。如果您希望继承完全反转,只需反转您的继承图,然后使用常规继承。
  • 当“只是反转继承图”时,应该提到可以使用__subclasscheck__(即根据需要在元类中覆盖它)来保留最初预期的“is-A”关系类和子类。
猜你喜欢
  • 2011-08-04
  • 2018-02-08
  • 1970-01-01
  • 2021-05-06
  • 1970-01-01
  • 2020-05-30
  • 2010-09-25
  • 2014-02-13
  • 1970-01-01
相关资源
最近更新 更多