以编程方式扩展数据类' __repr__

bad*_*ian 7 python python-dataclasses

假设我有一个带有 set 方法的数据类。如何扩展 repr 方法,以便在调用 set 方法时它也会更新:

from dataclasses import dataclass
@dataclass
class State:
    A: int = 1
    B: int = 2
    def set(self, var, val):
        setattr(self, var, val)
Run Code Online (Sandbox Code Playgroud)

前任:

In [2]: x = State()

In [3]: x
Out[3]: State(A=1, B=2)

In [4]: x.set("C", 3)

In [5]: x
Out[5]: State(A=1, B=2)

In [6]: x.C
Out[6]: 3
Run Code Online (Sandbox Code Playgroud)

我想要的结果

In [7]: x
Out[7]: State(A=1, B=2, C=3)
Run Code Online (Sandbox Code Playgroud)

Blc*_*ght 5

装饰dataclass器可让您快速轻松地构建具有在定义类时预先确定的特定字段的类。然而,您打算使用类的方式与数据类的用途并不太匹配。您希望能够在类已经存在之后动态添加新字段,并让它们使用各种方法(例如__init____repr__和大概__eq__)。这消除了几乎所有使用dataclass. 相反,您应该只编写自己的类来执行您希望它执行的操作。

这是一个快速而肮脏的版本:

class State:
    _defaults = {"A": 1, "B": 2}
    
    def __init__(self, **kwargs):
        self.__dict__.update(self._defaults)
        self.__dict__.update(kwargs)
        
    def __eq__(self, other):
        return self.__dict__ == other.__dict__ # you might want to add some type checking here
        
    def __repr__(self):
        kws = [f"{key}={value!r}" for key, value in self.__dict__.items()]
        return "{}({})".format(type(self).__name__, ", ".join(kws))
Run Code Online (Sandbox Code Playgroud)

这与您从 获得的非常相似types.SimpleNamespace,因此您可能可以使用它来代替(尽管它不执行默认值)。

您可以将您的set方法添加到这个框架中,尽管在我看来,这就像setattr您已经用来实现它的内置函数的不必要的重复。如果调用者需要动态设置属性,可以调用setattr自己。如果属性名称是常量,则可以使用普通的属性赋值语法s.foo = "bar"

  • 我会更进一步并建议这确实应该是一个“dict”。IMO 要么你提前知道全套属性,要么你不知道;尽管 Python 允许您忽略这种区别。 (3认同)