Generic types

Dictionary

The dictionary types like dict, typing.Dict correspond to a map pattern in serialized format (e.g. JSON map). perde supports the following form of dictionary types:

  • dict
  • typing.Dict
  • typing.Dict[X, Y]
  • dict[X] (since Python 3.9)
  • dict[X, Y] (since Python 3.9)

Using built-in dict,

>>> @dataclass
... class A:
...     a: str
...     b: dict

>>> perde.json.loads_as(A, '{"a": "x", "b": {"x": 3, "y": "hey", "z": true}}')
A(a='x', b={'x': 3, 'y': 'hey', 'z': True})

Using bare typing.Dict,

>>> @dataclass
... class A:
...     a: str
...     b: typing.Dict

>>> perde.json.loads_as(A, '{"a": "x", "b": {"x": 3, "y": "hey", "z": true}}')
A(a='x', b={'x': 3, 'y': 'hey', 'z': True})

Using typing.Dict[X, Y],

>>> @dataclass
... class A:
...     a: str
...     b: typing.Dict[str, float]

>>> perde.json.loads_as(A, '{"a": "x", "b": {"x": 3.0, "y": 1.4, "z": 1.5}}')
A(a='x', b={'x': 3.0, 'y': 1.4, 'z': 1.5})

Using dict[X, Y],

>>> @dataclass
... class A:
...     a: str
...     b: dict[str, float] # doctest: +PY39

>>> perde.json.loads_as(A, '{"a": "x", "b": {"x": 3.0, "y": 1.4, "z": 1.5}}') # doctest: +PY39
A(a='x', b={'x': 3.0, 'y': 1.4, 'z': 1.5})

List

The list types like list, typing.List correspond to a list or array pattern in serialized format (e.g. JSON array). perde supports the following form of list types:

  • list
  • typing.List
  • typing.List[X]
  • list[X] (since Python 3.9)

Using built-in list,

>>> @dataclass
... class A:
...     a: str
...     b: list

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, "a", 3.3]}')
A(a='x', b=[1, 'a', 3.3])

Using bare typing.List,

>>> @dataclass
... class A:
...     a: str
...     b: typing.List

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, 2, 3]}')
A(a='x', b=[1, 2, 3])

Using typing.List[X],

>>> @dataclass
... class A:
...     a: str
...     b: typing.List[int]

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, 2, 3]}')
A(a='x', b=[1, 2, 3])

Using list[X],

>>> @dataclass
... class A:
...     a: str
...     b: list[int] # doctest: +PY39

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, 2, 3]}') # doctest: +PY39
A(a='x', b=[1, 2, 3])

Set

The set types like set, typing.Set correspond to a list or array pattern in serialized format (e.g. JSON array). perde supports the following form of set types:

  • set / frozenset
  • typing.Set / typing.FrozenSet
  • typing.Set[X] / typing.FrozenSet[X]
  • set[X] / frozenset[X] (since Python 3.9)

Using built-in set,

>>> @dataclass
... class A:
...     a: str
...     b: set

>>> perde.json.loads_as(A, '{"a": "x", "b": [true, 2, 3]}')
A(a='x', b={True, 2, 3})

Using bare typing.Set,

>>> @dataclass
... class A:
...     a: str
...     b: typing.Set

>>> perde.json.loads_as(A, '{"a": "x", "b": [true, 2, 3]}')
A(a='x', b={True, 2, 3})

Using typing.Set[X],

>>> @dataclass
... class A:
...     a: str
...     b: typing.Set[int]

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, 2, 3]}')
A(a='x', b={1, 2, 3})

Using set[X],

>>> @dataclass
... class A:
...     a: str
...     b: set[int] # doctest: +PY39

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, 2, 3]}') # doctest: +PY39
A(a='x', b={1, 2, 3})

frozenset and typing.FrozenSet work the same as set and typing.Set.

Tuple

The tuple types like tuple, typing.Tuple correspond to a list or array pattern in serialized format (e.g. JSON array). perde supports the following form of set types:

  • tuple
  • typing.Tuple
  • typing.Tuple[X, Y, ...]
  • tuple[X, Y, ...] (since Python 3.9)

Using built-in tuple,

>>> @dataclass
... class A:
...     a: str
...     b: tuple

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, true, "hello"]}')
A(a='x', b=(1, True, 'hello'))

Using bare typing.Tuple,

>>> @dataclass
... class A:
...     a: str
...     b: typing.Tuple

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, true, "hello"]}')
A(a='x', b=(1, True, 'hello'))

Using typing.Tuple[X, Y, ...],

>>> @dataclass
... class A:
...     a: str
...     b: typing.Tuple[int, bool, str]

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, true, "hello"]}')
A(a='x', b=(1, True, 'hello'))

Using tuple[X, Y, ...],

>>> @dataclass
... class A:
...     a: str
...     b: tuple[int, bool, str] # doctest: +PY39

>>> perde.json.loads_as(A, '{"a": "x", "b": [1, true, "hello"]}') # doctest: +PY39
A(a='x', b=(1, True, 'hello'))

Empty tuple

Use typing.Tuple[()] to explicitly specify the empty tuple.

>>> @dataclass
... class A:
...     a: str
...     b: typing.Tuple[()]

>>> perde.json.loads_as(A, '{"a": "x", "b": []}')
A(a='x', b=())

tuple[()] is also available since Python 3.9.

Optional

typing.Optional allows to parse the field optionally.

>>> @dataclass
... class A:
...     a: str
...     b: typing.Optional[str]

>>> perde.json.loads_as(A, '{"a": "x"}')
A(a='x', b=None)

If the format supports None value (e.g. null in JSON), the None value is accepted.

>>> perde.json.loads_as(A, '{"a": "x", "b": null}')
A(a='x', b=None)

Note that serialization results include null explicitly.

>>> perde.json.dumps(A(a='x', b=None))
'{"a":"x","b":null}'

As the other generic types, typing.Optional without subscription is supported,

>>> @dataclass
... class A:
...     a: str
...     b: typing.Optional

>>> perde.json.loads_as(A, '{"a": "x"}')
A(a='x', b=None)

Union

typing.Union is used when there're multiple possible types for one field.

>>> @dataclass
... class A:
...     a: str
...     b: typing.Union[str, int]

>>> perde.json.loads_as(A, '{"a": "x", "b": 3}')
A(a='x', b=3)

>>> perde.json.loads_as(A, '{"a": "x", "b": "three"}')
A(a='x', b='three')

Bare typing.Union accepts anything including None value, also making the field optional.

>>> @dataclass
... class A:
...     a: str
...     b: typing.Union

>>> perde.json.loads_as(A, '{"a": "x", "b": "anything"}')
A(a='x', b='anything')

The field is optional.

>>> perde.json.loads_as(A, '{"a": "x"}')
A(a='x', b=None)

The field accepts None value.

>>> perde.json.loads_as(A, '{"a": "x", "b": null}')
A(a='x', b=None)

typing.Union cannot be used in schema-less formats which don't have type information themselves.

Any

typing.Any accepts any types including None, also making the field optional.

>>> @dataclass
... class A:
...     a: str
...     b: typing.Any

>>> perde.json.loads_as(A, '{"a": "x", "b": "anything"}')
A(a='x', b='anything')

The field is optional.

>>> perde.json.loads_as(A, '{"a": "x"}')
A(a='x', b=None)

The field accepts None value.

>>> perde.json.loads_as(A, '{"a": "x", "b": null}')
A(a='x', b=None)

Bare typing.Optional and typing.Any behave exactly same as typing.Any. typing.Any cannot be used in schema-less formats which don't have type information themselves.