将 dict 变成 TypedDict 的好方法?

Nice way to turn a dict into a TypedDict?

我想要一个很好的(mypy --strict 和 pythonic)方法来将无类型的 dict(来自 json.loads())转换为 TypedDict。我目前的做法是这样的:

class BackupData(TypedDict, total=False):
    archive_name: str
    archive_size: int
    transfer_size: int
    transfer_time: float
    error: str


def to_backup_data(data: Mapping[str, Any]) -> BackupData:
    result = BackupData()
    if 'archive_name' in data:
        result['archive_name'] = str(data['archive_name'])
    if 'archive_size' in data:
        result['archive_size'] = int(data['archive_size'])
    if 'transfer_size' in data:
        result['transfer_size'] = int(data['transfer_size'])
    if 'transfer_time' in data:
        result['transfer_time'] = int(data['transfer_time'])
    if 'error' in data:
        result['error'] = str(data['error'])
    return result

即我有一个带有可选键的 TypedDict 并且想要一个 TypedDict 实例。

上面的代码是冗余的和非功能性的(就函数式编程而言),因为我必须写四次名称,输入两次并且 result 必须是可变的。 遗憾的是 TypedDict 不能有方法,否则我可以写 s.th。喜欢

backup_data = BackupData.from(json.loads({...}))

关于 TypeDict 我是否遗漏了什么?这可以用一种漂亮的、非冗余的方式编写吗?

当您使用 TypedDict 时,所有信息都存储在 __annotations__ 字段中。

以你的例子为例:

BackupData.__annotations__

returns:

{'archive_name': <class 'str'>, 'archive_size': <class 'int'>, 'transfer_size': <class 'int'>, 'transfer_time': <class 'float'>, 'error': <class 'str'>}

现在我们可以使用该字典迭代数据并使用值进行类型转换:

def to_backup_data(data: Mapping[str, Any]) -> BackupData:
    result = BackupData()
    for key, key_type in BackupData.__annotations__.items():
        if key not in data:
            raise ValueError(f"Key: {key} is not available in data.")
        result[key] = key_type(data[key])
    return result

请注意,当数据不可用时我会抛出一个错误,这可以根据您的判断进行更改。

使用以下测试代码:

data = dict(
        archive_name="my archive",
        archive_size="50",
        transfer_size="100",
        transfer_time="2.3",
        error=None,
)

for key, value in result.items():
    print(f"Key: {key.ljust(15)}, type: {str(type(value)).ljust(15)}, value: {value!r}")

结果将是:

Key: archive_name   , type: <class 'str'>  , value: 'my archive'
Key: archive_size   , type: <class 'int'>  , value: 50
Key: transfer_size  , type: <class 'int'>  , value: 100
Key: transfer_time  , type: <class 'float'>, value: 2.3
Key: error          , type: <class 'str'>  , value: 'None'