5
votes

Le moyen le plus simple de copier tous les champs d'une instance de classe de données à une autre?

Supposons que vous ayez défini une classe de données Python:

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)

Quelle est la manière la plus simple de copier les valeurs d'une instance marker_a vers une autre instance marker_b ?

Voici un exemple de ce que j'essaye d'accomplir:

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

En tant que condition aux limites, je ne veux pas créer et affecter une nouvelle instance à marker_b . OK, je pourrais parcourir tous les champs définis et copier les valeurs une par une, mais il doit y avoir un moyen plus simple, je suppose.


0 commentaires

4 Réponses :


3
votes
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)

0 commentaires

6
votes

Je pense que faire une boucle sur les champs est probablement le moyen le plus simple. Toutes les autres options auxquelles je peux penser impliquent la création d'un nouvel objet.

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)


2 commentaires

Merci. C'est similaire à ce que je fais en ce moment, mais cela ne me semble pas pythonique.


C'est un cas d'utilisation un peu inhabituel. Le module dataclasses semble principalement supposer que vous serez heureux de créer un nouvel objet. Je recommanderais de coller ceci (ou ce que vous avez) dans une fonction et de passer à autre chose. Une chose à laquelle il convient de réfléchir est ce que vous voulez faire si l'un de vos arguments est en fait une sous-classe de Marker avec des champs supplémentaires.



3
votes

La fonction dataclasses.replace renvoie une nouvelle copie de l'objet. Sans transmettre aucun changement, il renverra une copie sans 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)

Notez qu'il s'agit d'une copie superficielle.


0 commentaires

0
votes

Une autre option qui peut être plus élégante:

import dataclasses

marker_a = Marker(1.0, 2.0)
marker_b = Marker(**dataclasses.asdict(marker_a))


1 commentaires

Merci, mais ma condition limite était de ne pas créer une nouvelle instance. Dans mon cas particulier, un autre objet pointe vers cette instance particulière, donc une nouvelle instance n'est pas une option.