美文网首页
pythonic-equivalence-of-go-struc

pythonic-equivalence-of-go-struc

作者: 9_SooHyun | 来源:发表于2023-03-13 16:24 被阅读0次
    from dataclasses import dataclass, field
    from typing import List, Dict, Any, Tuple, Type, TypeVar, Mapping, Union
    import dacite
    
    T = TypeVar("T")
    
    # reconstruct data by changing data's key to new key from key_referrence in recursion
    def _rekey_dict(key_referrence: Mapping, data: Mapping) -> Dict[str, Any]:
    
        # _rekey_list is a helper function to rekey any sub dicts in list or tuple
        def _rekey_list(key_referrence: Mapping, data: Union[List, Tuple]) -> List:
            res = []
            for v in data:
                if isinstance(v, dict):
                    res.append(_rekey_dict(key_referrence, v))
                elif isinstance(v, (List, Tuple)):
                    res.append(_rekey_list(key_referrence, v))
                else:
                    res.append(v)
            return res
    
        res = {}
        for k, v in data.items():
            if k in key_referrence:
                new_k = key_referrence[k]
            else:
                new_k = k
    
            if isinstance(v, dict):
                res[new_k] = _rekey_dict(key_referrence, v)
            elif isinstance(v, (List, Tuple)):
                res[new_k] = _rekey_list(key_referrence, v)
            else:
                res[new_k] = v
        return res
    
    
    def from_dict(data_class: Type[T], origin_data: dict, metadata_tag: str="") -> T:
    
        if not metadata_tag:
            return dacite.from_dict(data_class=data_class, data=origin_data)
    
        # metadata_tag to class field mapping
        tag2field = {}
        for field_name, field_attr in data_class.__dataclass_fields__.items():
            if metadata_tag in field_attr.metadata:
                tag2field[field_attr.metadata[metadata_tag]] = field_name
            else:
                tag2field[field_name] = field_name
        # print(tag2field)
    
        final_data = _rekey_dict(tag2field, origin_data)
        # print(final_data)
        return dacite.from_dict(data_class=data_class, data=final_data)
    
    @dataclass
    class Address:
        street: str = field(default="", metadata={"json_tag": "Street"})
        number: int = field(default=0, metadata={"json_tag": "Number"})
    
    
    @dataclass
    class Person:
        male: bool = field(default=True)
        name: str = field(default="", metadata={"json_tag": "Name"})
        age: int = field(default=0, metadata={"json_tag": "Age"})
        address: List[Address] = field(default_factory=lambda: list(), metadata={"json_tag": "Address"})
    
    
    if __name__ == "__main__":
        data = {
            "Name": "hh",
            "Age": 10,
            "Address": [{
                "Street": "s1",
                "Number": 99
            }]
        }
        p = from_dict(data_class=Person, metadata_tag="json_tag", origin_data=data)
        print(p) # Person(male=True, name='hh', age=10, address=[Address(street='', number=0)])
        # 在from_dict中构造tag2field的时候目前只能拿到顶层field的metadata
        # Person.address 嵌套的Address类内部字段Street和Number的metadata拿不到,所以`address=[Address(street='', number=0)]`
    
        
    
    

    相关文章

      网友评论

          本文标题:pythonic-equivalence-of-go-struc

          本文链接:https://www.haomeiwen.com/subject/oamcrdtx.html