Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type-checked conversion between Dataclasses and TypedDicts

I have a bunch of @dataclasses and a bunch of corresponding TypedDicts, and I want to facilitate smooth and type-checked conversion between them.

For example, consider

from dataclasses import dataclass
from typing_extensions import TypedDict

@dataclass
class Foo:
    bar: int
    baz: int
   
    @property
    def qux(self) -> int:
        return self.bar + self.baz

class SerializedFoo(TypedDict):
    bar: int
    baz: int
    qux: int

To create a serialization function, I can write something like

def serialize(foo: Foo) -> SerializedFoo:
    return SerializedFoo(
        bar=foo.bar,
        baz=foo.baz,
        qux=foo.qux,
    )

but doing this for many types becomes tedious, and every time I update the types, I also have to update the serialization function.

I can also do something like

import dataclasses

def serialize(foo: Foo) -> SerializedFoo:
    return SerializedFoo(**dataclasses.asdict(foo))

but this doesn't type check; mypy complains that it Expected keyword arguments, {...}, or dict(...) in TypedDict constructor.

Theoretically, it should be possible for a sufficiently smart type-checker to know that the dataclass has the properties needed to initialize the typed dictionary, but of course the usage of asdict makes this impossible in practice.

Is there a better way to convert a dataclass to a TypedDict with corresponding fields, that lets me both have the type checker tell me when something is wrong, and not have to type out every field in the conversion?

like image 444
Tomas Aschan Avatar asked Apr 07 '21 11:04

Tomas Aschan


People also ask

Why use typeddict type definitions?

The motivation here is simplicity. TypedDict type definitions could plausibly used to perform runtime type checking of dictionaries. For example, they could be used to validate that a JSON object conforms to the schema specified by a TypedDict type.

How to add methods to typeddict types?

TypedDict objects are regular dictionaries at runtime, and TypedDict cannot be used with other dictionary-like or mapping-like classes, including subclasses of dict. There is no way to add methods to TypedDict types. The motivation here is simplicity. TypedDict type definitions could plausibly used to perform runtime type checking of dictionaries.

What are the two ways to convert data type in C++?

In C++, it can be done by two ways: Converting by assignment: This is done by explicitly defining the required type in front of the expression in parenthesis. This can be also considered as forceful casting. Syntax: (type) expression. where type indicates the data type to which the final result is converted. Example:

How many types of type conversion are there?

There are two types of type conversion: Implicit Type Conversion Also known as ‘automatic type conversion’. Done by the compiler on its own, without any external trigger from the user. Generally takes place when in an expression more than one data type is present.


1 Answers

TypedDict is a regular dict at runtime, not a real class, doesn't do any type-checking, and is only for type hinting purposes. So, you can simply use typing.cast (docs here):

import dataclasses
from typing import cast

def serialize(foo: Foo) -> SerializedFoo:
    return cast(SerializedFoo, dataclasses.asdict(foo))

which will make Python type-checkers happy.

You could also check out the dacite library. It has some nifty things for stuff like this without the overkill of marshmallow. It allows some kind of type casting in its asdict/from_dict functions.

like image 168
Jeremyfx Avatar answered Nov 10 '22 00:11

Jeremyfx