获取Python 3中超类的类型

Nei*_*eil 6 python inheritance super superclass

我有一个基类,其中有两个派生自它的类。我希望基类的方法的行为有所不同,具体取决于参数是否与派生类具有相同的类型,或者只是基类的实例但类型不同。这是当前的实现:

class MyBase:
    def __init__(self, foo: int):
        self.foo = foo 

    def __eq__(self, other):
        return self.foo == other.foo 


class MyDerived_1(MyBase):
    def __init__(self, foo: int, bar: int):
        super().__init__(foo)
        self.bar = bar


class MyDerived_2(MyBase):
    def __init__(self, foo: int, bar: int):
        super().__init__(foo)
        self.bar = bar 

    def __eq__(self, other):
        if type(other) == type(self):
            return self.bar == other.bar 
        elif isinstance(other, MyBase):
            return super().__eq__(other)
        else:
            return False
Run Code Online (Sandbox Code Playgroud)

在倒数第四行中,我必须明确引用 MyBase。也许这很好,但我的理解是“super”关键字的一个要点是它应该允许您更改基类,而不必重新编写类中的任何内容。因此,该解决方案的一个潜在问题是,如果 MyBase 发生更改,那么init会很好,因为它调用“super”,但eq不会更新其行为。

所以我尝试用“type(super)”或“type(super())”替换“MyBase”,但这些不引用超类,它们引用对象“super”的类。

请注意,此问题不同于:

获取父类名? 获取Python 3等中未绑定方法对象的定义类。

因为一旦对象被初始化,他们就会寻找父类。

我想我应该能够通过运行 MRO 找到超级类。但这似乎是一个糟糕的解决方案,因为我不是在寻找整个继承树,我只是想知道超类的类型。

有没有办法从“超级”中提取这些信息?

Mar*_*ers 3

首先,你要在遇到不支持的类型时返回NotImplemented__eq__这样Python也可以给第二个操作数一个参与相等测试的机会。来自Python 数据模型文档

如果数字方法和丰富的比较方法没有实现所提供操作数的操作,则应返回此值。(然后解释器将尝试反射操作,或其他一些后备,具体取决于操作员。)

您的代码实际上应该只委托给super().__eq__()何时other不是同一类型的实例,这里不需要测试基类型;基类应该已经负责测试正确的类型或协议。

接下来,您可以使用Python 3__class__闭包来访问定义了方法的类;每当您在嵌套在类定义内的函数定义中使用super()或时,Python 都会添加此闭包:__class__

class MyBase:
    # ...

    def __eq__(self, other):
        if not isinstance(other, __class__):
            # we can't handle the other type, inform Python
            return NotImplemented
        return self.foo == other.foo 

class MyDerived_2(MyBase):
    # ...

    def __eq__(self, other):
        if isinstance(other, __class__):
            # if other is an instance of MyDerived_2, only test for 'bar'
            return self.bar == other.bar 
        # otherwise fall back to the base behaviour
        return super().__eq__(other)
Run Code Online (Sandbox Code Playgroud)

请注意,我使用isinstance()而不是type()测试,您希望的子类MyDerived_2继承此行为。

您还可以依赖鸭子类型,而不是测试特定的类层次结构;如果另一个对象具有正确的属性名称,则假设它可以用于与以下对象进行比较:

class MyBase:
    # ...

    def __eq__(self, other):
        try:
            self.foo == other.foo
        except AttributeError:
            # we can't handle the other type, inform Python
            return NotImplemented

class MyDerived_2(MyBase):
    # ...

    def __eq__(self, other):
        try:
            self.bar == other.bar
        except AttributeError:
            # otherwise fall back to the base behaviour
            return super().__eq__(other)
Run Code Online (Sandbox Code Playgroud)