如何使数据类实例属性非 public 和 __init__ arg?
How to make dataclass instance attribute non-public and an __init__ arg?
如果我希望实例属性为:
- 非public(也就是有一个前导下划线)
- 作为
__init__
签名中的参数
通常,我会这样做:
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我无法添加评论,所以我添加了一个新答案。
如果我希望实例属性为:
- 非public(也就是有一个前导下划线)
- 作为
__init__
签名中的参数
通常,我会这样做:
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我无法添加评论,所以我添加了一个新答案。