我应该如何为 python 中的 class 层次结构定义 __repr__?

How should I define a __repr__ for a class hierarchy in python?

给定以下代码:

class Base:

    __slots__ = 'base_field',

    def __init__(self, base_field):
        self.base_field = base_field

    def __repr__(self):
        return f"{self.__class__.__name__}(base_field={self.base_field!r})"

class Derived(Base):

    __slots__ = 'derived_field',

    def __init__(self, derived_field, **kwargs):
        super().__init__(**kwargs)
        self.derived_field = derived_field

    def __repr__(self):
        return f"{self.__class__.__name__}(base_field={self.base_field!r}, derived_field={self.derived_field!r})"


b1 = Base('base')
print(repr(b1))
b2 = eval(repr(b1))

d1 = Derived(base_field='dbase', derived_field='derived')
print(repr(d1))
d2 = eval(repr(d1))

如果我向 Base 添加新字段而忘记更新 Derived class' __repr__.

,此代码将会中断

我应该如何以及在何处定义 __repr__ 方法,以便:

我正在阅读一些代码,其中作者通过 Derived class __repr__ 调用 super().__repr__() 解决了问题,然后删除最后一个括号并附加字符串的 Derived 属性。这可以完成工作,但我想知道是否有更 Pythonic 的方法来实现这个目标。或者,也许这就是 Pythonic 方式?

我调查了 Python 的 dataclass,这在构建做正确事情的 __repr__ 方面做得很好。然而,他们使用 __dict__s,因此需要权衡取舍(例如,一次具有多个实例的对象的内存占用空间较小,而 __repr__ 更简单)。

谢谢!

您可以动态地执行此操作,只需反省插槽并确保执行 MRO:

class Base:

    __slots__ = 'base_field',

    def __init__(self, base_field):
        self.base_field = base_field

    def __repr__(self):
        slots = [
            slot
            for klass in type(self).mro() if hasattr(klass, '__slots__')
            for slot in klass.__slots__
        ]
        args = ", ".join(
            [f"{slot}={getattr(self, slot)!r}" for slot in reversed(slots)]
        )
        return f"{type(self).__name__}({args})"

class Derived(Base):

    __slots__ = 'derived_field',

    def __init__(self, derived_field, **kwargs):
        super().__init__(**kwargs)
        self.derived_field = derived_field

您可以将它放在一个函数中,然后在各种层次结构中重复使用代码。

    def slotted_repr(obj):
        slots = [
            slot
            for klass in type(obj).mro() if hasattr(klass, '__slots__')
            for slot in klass.__slots__
        ]
        args = ", ".join(
            [f"{slot}={getattr(obj, slot)!r}" for slot in reversed(slots)]
        )
        return f"{type(obj).__name__}({args})"