简体   繁体   中英

populate dataclass instance from other dataclass instance

I have a scenario where I've two dataclass which share some command keys. Let's say

@dataclass
class A
key1: str = ""
key2: dict = {}
key3: Any = ""

and class B

@dataclass
class B
key1: str = ""
key3: Any = ""
key4: List = []

Both of this class share some key value. Now I want to assign those common key value from class A to to class B instance.

One way I know is to convert both the class to dict object do the process and convert it back to dataclass object. But from what I know sole purpose of dataclass is to effectively store data and manage. I believe there is some better approach.

Expected Input and Output

# State of dataclass A while B is not initialized
A(key1: "key1value", Key2: {"a": "a"}, key3: [1,2])
# State of B should be
B(key1: "key1value",key3: [1,2], key4: [])

You can use signature to get all the attributes of your class, then getattr to check if keys have the same name, and finally setattr to change the value of classB , something like this:

from dataclasses import dataclass, field
from inspect import signature
from typing import Any, List, Dict

def main():
  instanceA, instanceB = A("key1value", {"a": "a"}, [1,2]), B()
  attrsA, attrsB = signature(A).parameters, signature(B).parameters

  for attr in attrsA:
    if attr in attrsB:
      valueA = getattr(instanceA, attr)
      setattr(instanceB, attr, valueA)

@dataclass
class A:
  key1: str = ""
  key2: Dict[str, str] = field(default_factory=dict)
  key3: Any = ""

@dataclass
class B:
  key1: str = ""
  key3: Any = ""
  key4: List = field(default_factory=list)


if __name__ == '__main__':
  main()

Instances before assign:

A(key1='key1value', key2={'a': 'a'}, key3=[1, 2])
B(key1='', key3='', key4=[])

After:

A(key1='key1value', key2={'a': 'a'}, key3=[1, 2])
B(key1='key1value', key3=[1, 2], key4=[])

If you add a method to your class, you can do it for any class and have it for A to B or B to A.

The problem I see with signature is that if you make any change in init , for example having for B the class A as parameter, it will not work anymore.

from dataclasses import dataclass, field, fields
from typing import Any

@dataclass
class A:
    key1: str = ""
    key2: dict = field(default_factory=dict)
    key3: Any = ""

    def set_keys(self, other):
        self_fields = list(map(lambda x: x.name,  fields(self)))
        for _field in fields(other):
            if _field.name in self_fields:
                setattr(self, _field.name, getattr(other, _field.name))

@dataclass
class B:
    key1: str = ""
    key3: Any = ""
    key4: list = field(default_factory=list)

    def set_keys(self, other):
        self_fields = list(map(lambda x: x.name,  fields(self)))
        for _field in fields(other):
            if _field.name in self_fields:
                setattr(self, _field.name, getattr(other, _field.name))

a = A(key1="key1value", key3=[1, 2])
b = B()

b.set_keys(a)

Result:

A(key1='key1value', key2={'a': 'a'}, key3=[1, 2])
B(key1='key1value', key3=[1, 2], key4=[])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM