konradhalas / dacite

Simple creation of data classes from dictionaries.
MIT License
1.76k stars 106 forks source link

Additional tagging options for enum representation #99

Closed malthe closed 4 years ago

malthe commented 4 years ago

In Rust's serde library, enum representation can be untagged (as currently supported in dacite), but also internally and externally tagged.

That is, it would be useful to have a means of disambiguating a polymorphic type based not on structure, but a tagged value.

For example:

from dataclasses import dataclass, field
from enum import Enum

class E(Enum):
    X = 'x'
    Y = 'y'
    Z = 'z'

@dataclass
class A:
    kind: E = field(init=False)

@dataclass
class B(A):
    kind = E.X

@dataclass
class C(A):
    kind = E.Y

The library would be able to use "kind" as an internal tagging:

assert dacite.from_dict({"kind": "x"}) == B()

External tagging is probably harder to do without using annotations.

camerongraybill commented 4 years ago

I've been using a literal for this - for example

from dataclasses import dataclass, field
from typing import Literal, Union
import dacite

@dataclass
class B:
    kind: Literal["x"] = "x"

@dataclass
class C:
    kind: Literal["y"] = "y"

A = Union[B, C]
@dataclass
class _UnionHelper:
    data: A

assert dacite.from_dict(_UnionHelper, {"data": {"kind": "x"}}).data == B()

This is somewhat similar to https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions which is where I got the idea from.

konradhalas commented 4 years ago

Hi @malthe - thank you for reporting this issue.

As @camerongraybill you should use Literal here. It's a way to go :)