Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easiest way to copy all fields from one dataclass instance to another?

Tags:

Let's assume you have defined a Python dataclass:

@dataclass
class Marker:
    a: float
    b: float = 1.0

What's the easiest way to copy the values from an instance marker_a to another instance marker_b?

Here's an example of what I try to achieve:

marker_a = Marker(1.0, 2.0)
marker_b = Marker(11.0, 12.0)
# now some magic happens which you hopefully can fill in
print(marker_b)
# result: Marker(a=1.0, b=2.0)

As a boundary condition, I do not want to create and assign a new instance to marker_b.

OK, I could loop through all defined fields and copy the values one by one, but there has to be a simpler way, I guess.

like image 363
Tom Pohl Avatar asked Sep 16 '19 18:09

Tom Pohl


4 Answers

The dataclasses.replace function returns a new copy of the object. Without passing in any changes, it will return a copy with no modification:

>>> import dataclasses
>>> @dataclasses.dataclass
... class Dummy:
...     foo: int
...     bar: int
... 
>>> dummy = Dummy(1, 2)
>>> dummy_copy = dataclasses.replace(dummy)
>>> dummy_copy.foo = 5
>>> dummy
Dummy(foo=1, bar=2)
>>> dummy_copy
Dummy(foo=5, bar=2)

Note that this is a shallow copy.

Edit to address comments:

If a copy is undesirable, I would probably go with the following:

for key, value in dataclasses.asdict(dummy).items():
    setattr(some_obj, key, value)
like image 54
Daniel Perez Avatar answered Sep 26 '22 21:09

Daniel Perez


I think that looping over the fields probably is the easiest way. All the other options I can think of involve creating a new object.

from dataclasses import fields

marker_a = Marker(5)
marker_b = Marker(0, 99)

for field in fields(Marker):
    setattr(marker_b, field.name, getattr(marker_a, field.name))

print(marker_b)  # Marker(a=5, b=1.0)
like image 42
Patrick Haugh Avatar answered Sep 27 '22 21:09

Patrick Haugh


@dataclass
class Marker:
    a: float
    b: float = 1.0

marker_a = Marker(0.5)

marker_b = Marker(**marker_a.__dict__)

marker_b

# Marker(a=0.5, b=1.0)

If you didn't want to create a new instance, try this:

marker_a = Marker(1.0, 2.0)
marker_b = Marker(11.0, 12.0)

marker_b.__dict__ = marker_a.__dict__.copy()

# result: Marker(a=1.0, b=2.0)

Not sure whether that's considered a bad hack though...

like image 6
r.ook Avatar answered Sep 24 '22 21:09

r.ook


Another option which may be more elegant:

import dataclasses

marker_a = Marker(1.0, 2.0)
marker_b = Marker(**dataclasses.asdict(marker_a))
like image 3
Nur L Avatar answered Sep 26 '22 21:09

Nur L