DictDataClass

class privex.helpers.collections.DictDataClass[source]

This is a base class for use with Python 3.7+ dataclass ‘s, designed to make dataclasses more interoperable with existing dictionaries, and allows them to be used like dictionaries, similar to Dictable, but more powerful / flexible.

The most notable difference between this and Dictable - is that DictDataClass uses the attribute raw_data on your dataclass to store any excess attributes when your dataclass is initialised from a dictionary with from_dict() or from_list(), allowing you to retrieve any dictionary keys which couldn’t be stored on your dataclass instance.

Basic Example:

>>> from dataclasses import dataclass, field
>>> from privex.helpers import DictDataClass, DictObject
>>> from typing import Union
>>>
>>> @dataclass
>>> class ExampleDataclass(DictDataClass):
...     example: str = 'hello world'
...     lorem: int = 999
...     raw_data: Union[dict, DictObject] = field(default_factory=DictObject, repr=False)
...     ### ^ The raw, unmodified data that was passed as kwargs, as a dictionary
...     ### For DictDataClass to work properly, you must include the raw_data dataclass field in your dataclass.
...
>>> edc = ExampleDataclass.from_dict(dict(example='test', hello='this is an example'))
>>> edc.example
'test'
>>> dict(edc)
{'example': 'test', 'lorem': 999}

Thanks to raw_data - you can access any extraneous items contained within the dictionary used in from_dict() as if they were part of your dataclass. You can also access and set any attribute using standard dictionary item syntax with square brackets like so:

>>> edc.hello               # This is not in the original dataclass attributes, but is proxied from raw_data
'this is an example'
>>> edc['hello']            # Also works with item / dict syntax
'this is an example'
>>> edc['hello'] = 'world'  # You can set attributes using "key" / dict-like syntax
>>> edc.hello
'world'
>>> edc.hello = 'test'      # You can also set raw_data keys using standard attribute dot-notation syntax.
>>> edc['hello']
'test'

Dictionary casting modes / ``__iter__`` configuration modes

There are a total of four (4) dict` conversion modes that you may use for a given class.

These modes control whether raw_data is used when an instance is being casted via dict(obj), the order in which it’s merged with the instance attributes, along with the option to include just the dataclass attributes, or just the raw_data.

Available DictConfig.dict_convert_mode options:

* **Fallback / Dataclass / Instance Attributes Only** - When ``dict_convert_mode`` is empty (``None`` or ``""``), or
  it can't be matched against a pre-defined conversion mode, when an instance is converted into a :class:`.dict` - only the
  attributes of the instance are used - :attr:`.raw_data` keys are ignored.

  ``dict_convert_mode`` settings: ``None``, ``""``, ``"none"`` or any other invalid option.

* Raw Data Only ( ``raw`` / ``raw_data`` ) - When this mode is used, converting the instance to a ``dict`` will
  effectively just return ``raw_data``, while still enforcing the ``dict_exclude`` and ``dict_listify`` settings.

  ``dict_convert_mode`` settings: ``"raw"``, ``"raw_data"``, ``"rawdata"``, or any other value beginning with ``raw``


* Merge with Dataclass Priority - In this mode, both :attr:`.raw_data` and the instance's attributes are used when converting
  into a :class:`.dict` - first the :attr:`.raw_data` dictionary is taken, and we merge the instance attributes on top of it.

  This means the instance/dataclass attributes take priority over the ``raw_data`` attributes, which will generally result
  in only ``raw_data`` keys which don't exist on the instance having their values used in the final ``dict``.

  ``dict_convert_mode`` settings: ``"merge_dc"`` / ``"merge_ins"`` / ``"merge_dataclass"``

* Merge with :attr:`.raw_data` Priority - In this mode, both :attr:`.raw_data` and the instance's attributes are used when
  converting into a :class:`.dict` - first the instance attributes are converted into a dict, then we merge the
  :attr:`.raw_data` dictionary on top of it.

  This means the :attr:`.raw_data` keys take priority over the instance/dataclass attributes, which will generally result in
  only instance attributes which don't exist in ``raw_data`` having their values used in the final ``dict``.

  ``dict_convert_mode`` settings: ``"merge_rd"`` / ``"merge_raw"`` / ``"merge_raw_data"``

By default, the conversion mode is set to merge_dc, which means when an instance is converted into a dict - the dataclass instance attributes are merged on top of the raw_data dictionary, meaning raw_data is included, but dataclass attributes take priority over raw_data keys.

Changing the conversion mode

>>> from dataclasses import dataclass, field
>>>
>>> @dataclass
>>> class MyDataclass(DictDataClass):
...     class DictConfig:
...         dict_convert_mode = 'merge_dc'
...
...     hello: str
...     lorem: str = 'ipsum'
...     raw_data: Union[dict, DictObject] = field(default_factory=DictObject, repr=False)
>>> dc = MyDataclass.from_dict(dict(hello='test', example=555, test='testing'))
>>> dc.hello
'test'
>>> dc.hello = 'replaced'
>>> dict(dc)
{'hello': 'replaced', 'example': 555, 'test': 'testing', 'lorem': 'ipsum'}
>>> dc.DictConfig.dict_convert_mode = 'merge_raw'
{'hello': 'test', 'lorem': 'ipsum', 'example': 555, 'test': 'testing'}
>>> dc.DictConfig.dict_convert_mode = None
{'hello': 'replaced', 'lorem': 'ipsum'}
__init__()

Initialize self. See help(type(self)) for accurate signature.

Methods

Methods

from_dict(obj)