Reputation: 1171
I'd like to replace the attributes of a dataclass instance, analogous to namedtuple._replace()
, i.e. making an altered copy of the original object:
from dataclasses import dataclass
from collections import namedtuple
U = namedtuple("U", "x")
@dataclass
class V:
x: int
u = U(x=1)
u_ = u._replace(x=-1)
v = V(x=1)
print(u)
print(u_)
print(v)
This returns:
U(x=1)
U(x=-1)
V(x=1)
How can I mimic this functionality in dataclass objects?
Upvotes: 37
Views: 37533
Reputation: 49
Just using replace will have reference pointer to previous mutable objects, hence two instances of a dataclass will share a state
So try something like this:
@dataclasses.dataclass(frozen=True)
class MyDataClass:
mutable_object: list
val: int
def copy(self, **changes):
return dataclasses.replace(deepcopy(self), **changes)
data = MyDataClass([], 1)
data2 = data.copy(val=2)
assert data.mutable_object != data2.mutable_object
Upvotes: 1
Reputation: 363233
The dataclasses
module has a helper function for field replacement on instances (docs)
from dataclasses import replace
Usage differs from collections.namedtuple
, where the functionality was provided by a method on the generated type (Side note: namedtuple._replace
is documented/public API, using an underscore on the name was called a "regret" by the author, see link at end of answer).
>>> from dataclasses import dataclass, replace
>>> @dataclass
... class V:
... x: int
... y: int
...
>>> v = V(1, 2)
>>> v_ = replace(v, y=42)
>>> v
V(x=1, y=2)
>>> v_
V(x=1, y=42)
For more background of the design, see the PyCon 2018 talk - Dataclasses: The code generator to end all code generators. The replace
API is discussed in depth, along with other design differences between namedtuple
and dataclasses
, and some performance comparisons are shown.
Upvotes: 59
Reputation: 1
@dataclass()
class Point:
x: float = dataclasses.Field(repr=True, default=0.00, default_factory=float, init=True, hash=True, compare=True,
metadata={'x_axis': "X Axis", 'ext_name': "Point X Axis"})
y: float = dataclasses.Field(repr=True, default=0.00, default_factory=float, init=True, hash=True, compare=True,
metadata={'y_axis': "Y Axis", 'ext_name': "Point Y Axis"})
Point1 = Point(13.5, 455.25)
Point2 = dataclasses.replace(Point1, y=255.25)
print(Point1, Point2)
Upvotes: -2
Reputation: 10365
I know the question is about dataclass
, but if you're using attr.s
instead then you can use attr.evolve
instead of dataclasses.replace
:
import attr
@attr.s(frozen=True)
class Foo:
x = attr.ib()
y = attr.ib()
foo = Foo(1, 2)
bar = attr.evolve(foo, y=3)
Upvotes: 0
Reputation: 77484
dataclass
is just syntactic sugar for the automatic creation of a special __init__
method and a host of other "boilerplate" methods based on type-annotated attributes.
Once the class is created, it is like any other, and its attributes can be overwritten and instances can be copied, e.g.
import copy
v_ = copy.deepcopy(v)
v_.x = -1
Depending on what the attributes are, you may only require copy.copy
.
Upvotes: -1