我应该如何为 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__
方法,以便:
- 当我将
Base
和 Derived
实例都传递给 repr()
时,它们的正确字符串将被返回(例如 repr(Base("base")) == "Base(base_field="base")
和 repr(Derived(base_field='dbase', derived_field='derived')) == "Derived(base_field="base", derived_field="derived")
- 我可以安全地将字段添加到
Base
class,这些字段将显示在 Derived
class' __repr__
输出中。换句话说,Derived
class' __repr__
函数不应该知道 Base
class. 中的字段
- 代码最少。如果我必须添加多于几行,额外的代码可能不值得
我正在阅读一些代码,其中作者通过 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})"
给定以下代码:
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__
方法,以便:
- 当我将
Base
和Derived
实例都传递给repr()
时,它们的正确字符串将被返回(例如repr(Base("base")) == "Base(base_field="base")
和repr(Derived(base_field='dbase', derived_field='derived')) == "Derived(base_field="base", derived_field="derived")
- 我可以安全地将字段添加到
Base
class,这些字段将显示在Derived
class'__repr__
输出中。换句话说,Derived
class'__repr__
函数不应该知道Base
class. 中的字段
- 代码最少。如果我必须添加多于几行,额外的代码可能不值得
我正在阅读一些代码,其中作者通过 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})"