如何使数据类实例属性非 public 和 __init__ arg?

How to make dataclass instance attribute non-public and an __init__ arg?

如果我希望实例属性为:

通常,我会这样做:

class Foo:
    def __init__(self, bar: str):
        self._bar = bar

foo = Foo(bar="bar")  # foo.bar would raise an AttributeError

但是,在 dataclasses 中,我不确定该怎么做。

from dataclasses import dataclass

@dataclass
class Foo:
    bar: str  # This leaves bar as a public instance attribute

dataclasses.dataclass 中执行此操作的正确方法是什么?

对于 InitVar__post_init__ 方法,这是一个相对简单的案例。 (尽管冗长程度可能不是您想要的。)

from dataclasses import dataclass, InitVar


@dataclass
class Foo:
    bar: InitVar[str]

    def __post_init__(self, bar):
        self._bar = bar

这与您描述的一样。 bar(如所写)是 __init__ 的必需参数,但不会生成名为 bar.

的属性
>>> f = Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'bar'
>>> f = Foo(9)
>>> f._bar
9
>>> f.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'

明确地说,这是您将在 现有 数据类中使用的东西;你不会选择创建一个数据类来做到这一点。

如果您希望它成为一个 __init__() 参数,只需编写您自己的参数即可,这样可以防止自动生成一个参数。请注意,如下所示指定 init=False 并不是真正必需的,因为它无论如何都不会发生,但似乎是引起人们注意正在发生的事情的好方法。出于同样的原因,为私有 _bar 字段指定 field(init=False) 是多余的。

from dataclasses import dataclass, field


@dataclass(init=False)
class Foo:
    def __init__(self, bar: str):
        self._bar = bar

    _bar: str = field(init=False)


foo = Foo(bar="xyz")
print(foo._bar)  # -> xyz
print(foo.bar)  # -> AttributeError: 'Foo' object has no attribute 'bar'

你可以找到关于在数据类中使用 @property 的大讨论,它可以解决你的问题(_bar 在 setter/getter 下是安全的)。

__repr__asdict() 的输出显示不需要的属性可能存在一些问题,可以通过

解决

PS我无法添加评论,所以我添加了一个新答案。