【问题标题】:Extend dataclass' __repr__ programmatically以编程方式扩展数据类'__repr__
【发布时间】:2021-04-30 02:03:37
【问题描述】:

假设我有一个带有 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)

例如:

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

我想要的结果

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

【问题讨论】:

  • 您是否还需要扩展__init__ 方法以使State(A=1, B=2, C=3) 成为有效的构造函数调用?或者,如果您的 __repr__ 方法为您提供了一个您不能 eval 到相同对象的字符串,是否可以?
  • 这不应该是dataclass;各种各样的事情都会破裂。你知道types.SimpleNamespace吗?
  • 我觉得后者应该没问题。我在想的方式是我用不同的状态调用初始化的几个属性初始化另一个类模型。然后我从 Model 运行一些方法,我想在 State 创建的每个属性中创建一些新属性。
  • @o11c: 为什么它不应该是一个数据类?我使用它来存储某些模型的参数可能会在以后更新。我想一种方法是在初始化时定义参数,而不是稍后设置。
  • 我认为令人困惑的是,如果您在运行时扩展字段(例如,在定义类之后添加 C 属性),那么您不会处理类似记录的数据(具有固定字段)。相反,您的数据是动态的。没关系,这不是 dataclasses 的用途。

标签: python python-dataclasses


【解决方案1】:

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))

这与您从 types.SimpleNamespace 获得的内容非常相似,因此您可能只能使用它(虽然它不使用默认值)。

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

【讨论】:

  • 我会更进一步,建议这真的应该是dict。 IMO 要么您提前知道全套属性,要么您不知道;即使 Python 让你忽略这种区别。
  • 这当然是一种可能性,尽管在某些情况下使用属性语法很方便。虽然我猜大多数最好的情况是dataclass 首先可以工作的情况!
  • 所以我知道完整的属性集,但它们可能不是同一类型(列表与整数),或者我可能会在其他模型中忽略它们。如果是这种情况,那么您似乎会建议我只在 init 中定义所有参数,然后忽略我不需要的参数或覆盖不同类型的参数?我想另一种选择是始终使用列表,即使列表中的所有值都相同。
  • 我更喜欢 .超过[]
  • 我认为还有其他一些选择。一种是为您可能需要一起使用的每组属性指定单独的数据类。另一个是完全动态的,这就是我展示的。您指定所有内容的想法(使用默认值,或者可能None 用于未使用的属性)是第三种选择。
猜你喜欢
  • 2016-08-24
  • 1970-01-01
  • 2012-03-02
  • 1970-01-01
  • 2019-11-18
  • 2018-03-26
  • 2020-08-12
  • 2021-12-07
  • 1970-01-01
相关资源
最近更新 更多