Query Python dataclass object by key like I can in nested dictionary

Viewed 57

I have a nested dictionary:

D = {'ID_1': {'name': 'Julie', 'age' : 19}, 'ID_2': {'name': 'Andre', 'age': 25}}

And I can retrieve an individual dictionary:

D['ID_2']

I would like to rather use dataclasses. I can create a dataclass:

@dataclass
class User:
    name: str
    age: int

and then instantiate each ID:

ID_1 = User('Julie', 19)
ID_2 = User('Andre', 25)

But I would like to store all objects in one object and be able to query for a particular ID, just like I can when using dictionaries.

1 Answers

Since it's possible that the outer keys in the dict object might be dynamically populated (i.e. you could add an ID_3 field later, and you don't want the code to break) - I would suggest using a custom outer class, and a nested dataclass Person for this particular task , as shown below.

Note I would add a helper from_dict class method to make this a bit easier, rather than going through the constructor or __init__ method.

from dataclasses import dataclass


class MyClass:

    # not needed, but useful for type hinting purposes
    ID_1: 'Person'
    ID_2: 'Person'

    @classmethod
    def from_dict(cls, d: dict):
        o = cls()
        for k, v in d.items():
            setattr(o, k, Person(**v))

        return o

    def __repr__(self):
        fields = ', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())
        return f'{self.__class__.__qualname__}({fields})'


@dataclass
class Person:
    name: str
    age: int


data = {'ID_1': {'name': 'Julie', 'age': 19}, 'ID_2': {'name': 'Andre', 'age': 25}}
c = MyClass.from_dict(data)
print(c)

print(c.ID_1.name)  # Julie
print(c.ID_2.age)   # 25

Output:

MyClass(ID_1=Person(name='Julie', age=19), ID_2=Person(name='Andre', age=25))
Julie
25

Dot Access Dict

Another option which requires less boilerplate code, could be to use my helper library dotwiz, which enables dot or attribute-access for dict objects.

Note: this example requires pip install dotwiz.

import typing

from dotwiz import DotWiz


# define an alias
MyClass = DotWiz


if typing.TYPE_CHECKING:  # only runs for static type checkers

    class MyClass(DotWiz):
        # not needed, but useful for type hinting purposes
        ID_1: 'Person'
        ID_2: 'Person'

    # This is a stub class, only for type hinting purposes; it wouldn't
    # work if you wanted to define or use methods on the class.
    class Person:
        name: str
        age: int


data = {'ID_1': {'name': 'Julie', 'age': 19}, 'ID_2': {'name': 'Andre', 'age': 25}}
c = MyClass(data)
print(c)

print(c.ID_1.name)  # Julie
print(c.ID_2.age)   # 25

Out:

✫(ID_1=✫(name='Julie', age=19), ID_2=✫(name='Andre', age=25))
Julie
25

Full disclaimer: I am the creator and maintainer of this library.

Related