从字典初始化Python数据类

PIG*_*208 25 python

假设我想初始化以下数据类

from dataclasses import dataclass

@dataclass
class Req:
    id: int
    description: str
Run Code Online (Sandbox Code Playgroud)

我当然可以通过以下方式做到这一点:

data = make_request() # gives me a dict with id and description as well as some other keys.
                      # {"id": 123, "description": "hello", "data_a": "", ...}
req = Req(data["id"], data["description"])
Run Code Online (Sandbox Code Playgroud)

但是,考虑到我需要的键始终是字典的子集,我是否可以通过字典解包来完成此操作?

req = Req(**data)  # TypeError: __init__() got an unexpected keyword argument 'data_a'
Run Code Online (Sandbox Code Playgroud)

con*_*ger 14

这是一个可以普遍用于任何类的解决方案。它只是过滤输入字典以排除不是类字段名称的键init==True

from dataclasses import dataclass, fields

@dataclass
class Req:
    id: int
    description: str

def classFromArgs(className, argDict):
    fieldSet = {f.name for f in fields(className) if f.init}
    filteredArgDict = {k : v for k, v in argDict.items() if k in fieldSet}
    return className(**filteredArgDict)

data = {"id": 123, "description": "hello", "data_a": ""}
req = classFromArgs(Req, data)
print(req)
Run Code Online (Sandbox Code Playgroud)

输出:

Req(id=123, description='hello')
Run Code Online (Sandbox Code Playgroud)

更新:这是上述策略的一个变体,它创建一个实用程序类,dataclasses.fields为使用它的每个数据类进行缓存(由 @rv.kvetch 的评论提示,表达了对dataclasses.fields同一数据类的多次调用的重复处理的性能问题)。

Req(id=123, description='hello')
Run Code Online (Sandbox Code Playgroud)

输出:

Req(id=123, description='hello')
Req(id=456, description='goodbye')
Req2(id=123, description='hello', data_a='world')

Here's a peek at the internals of DataClassUnpack:
{<class '__main__.Req'>: {'description', 'id'}, <class '__main__.Req2'>: {'description', 'data_a', 'id'}}
Run Code Online (Sandbox Code Playgroud)

  • @rv.kvetch 请参阅更新的答案,其中显示了解决您的问题的一种方法。 (3认同)

Dav*_*izu 10

您可以引入一个新函数来执行从 dict 到数据类的给定转换:

import inspect
from dataclasses import dataclass

@dataclass
class Req:
    id: int
    description: str

def from_dict_to_dataclass(cls, data):
    return cls(
        **{
            key: (data[key] if val.default == val.empty else data.get(key, val.default))
            for key, val in inspect.signature(cls).parameters.items()
        }
    )

from_dict_to_dataclass(Req, {"id": 123, "description": "hello", "data_a": ""})
# Output: Req(id=123, description='hello')
Run Code Online (Sandbox Code Playgroud)

请注意,if val.default == val.empty需要条件来检查您的数据类是否设置了默认值。如果这是真的,那么我们在构造数据类时应该考虑给定的值。

  • 请注意,对于一般用途,“signature(Req)”应为“signature(cls)”。 (3认同)

小智 5

python 3.10 或更高版本


from dataclasses import dataclass

@dataclass(kw_only=True)
class Req:
    id: int
    description: str

# invalid keys will cause failure
Req(**{"id": 123, "description": "hello"})

Run Code Online (Sandbox Code Playgroud)