Reputation: 2391
If I want an instance attribute to be:
__init__
signatureNormally, I would do this:
class Foo:
def __init__(self, bar: str):
self._bar = bar
foo = Foo(bar="bar") # foo.bar would raise an AttributeError
However, in dataclasses
, I'm unsure how to do this.
from dataclasses import dataclass
@dataclass
class Foo:
bar: str # This leaves bar as a public instance attribute
What is the correct way to do this in dataclasses.dataclass
?
Upvotes: 3
Views: 5100
Reputation: 123423
If you want it to be an __init__()
argument, just write your own which will prevent one from being automatically generated. Note that specifying init=False
as shown below isn't really required since it wouldn't have happened anyway, but nevertheless seems like good way to draw attention to what's going on. For the same reason, specifying field(init=False)
for the private _bar
field is superfluous.
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'
Upvotes: 1
Reputation: 1049
Here you can find a big discussion on using @property
in dataclasses which can resolve your problem (the _bar
is safe under the setter/getter).
There could be some problems with unwanted attributes displayed as the output of __repr__
or asdict()
, which can be solved as here
PS I can't add a comment, so I've added a new answer.
Upvotes: 1
Reputation: 530960
This is a relatively simple case for an InitVar
and the __post_init__
method. (Though the verbosity is probably not what you had in mind.)
from dataclasses import dataclass, InitVar
@dataclass
class Foo:
bar: InitVar[str]
def __post_init__(self, bar):
self._bar = bar
This behaves as you described. bar
(as written) is a required argument to __init__
, but does not make an attribute named 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'
To be clear, this is something you would use in an existing dataclass; you wouldn't choose to create a dataclass just to do this.
Upvotes: 8