在带有 slot=True 的数据类中使用 super() 时出现类型错误

enk*_*tor 9 python slots python-dataclasses

我有一个带有(某种)getter 方法的数据类。

此代码按预期工作:

from dataclasses import dataclass

@dataclass()
class A:
    def get_data(self):
        # get some values from object's fields
        # do some calculations
        return "a calculated value"

@dataclass()
class B(A):
    def get_data(self):
        data = super().get_data()
        return data + " (modified)"


b = B()

print(b.get_data())  # a calculated value (modified)
Run Code Online (Sandbox Code Playgroud)

但是,如果我添加slots=True,我会得到TypeError

from dataclasses import dataclass

@dataclass(slots=True)
class A:
    def get_data(self):
        return "a calculated value"

@dataclass(slots=True)
class B(A):
    def get_data(self):
        data = super().get_data()
        return data + " (modified)"


b = B()

print(b.get_data())  # TypeError: super(type, obj): obj must be an instance or subtype of type
Run Code Online (Sandbox Code Playgroud)

如果我使用旧式 super() ,错误就会消失,与pep-3135相反:

from dataclasses import dataclass

@dataclass(slots=True)
class A:
    def get_data(self):
        return "a calculated value"

@dataclass(slots=True)
class B(A):
    def get_data(self):
        data = super(B, self).get_data()
        return data + " (modified)"


b = B()

print(b.get_data())  # a calculated value (modified)
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况以及如何以正确的方式解决它?

dec*_*eze 2

slots:如果为 true (默认为False),__slots__将生成属性并返回新类而不是原始类。如果__slots__已在类中定义,则TypeError引发。

https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass

以此为例:

@dataclass(slots=True)
class Foo:
    pass
Run Code Online (Sandbox Code Playgroud)

这意味着它的工作原理如下:

class Foo:
    pass

Foo = dataclass(slots=True)(Foo)
Run Code Online (Sandbox Code Playgroud)

您定义一个类Foo,然后它会被替换为另一个经过更改的类。

现在,你的方法:

def get_data(self):
    data = super().get_data()
    ...
Run Code Online (Sandbox Code Playgroud)

super()是在原始Foo类中编写的,并且假设它应该查找该原始Foo类的父类;但它当前拥有的实例实际上并不是该类的实例,而是另一个已更改类的实例。

当你这样做时:

data = super(Foo, self).get_data()
Run Code Online (Sandbox Code Playgroud)

这会查找当前引用的名称Foo,它再次匹配self此时也引用的内容。