Python Data Class From Dict

Python Data Class From Dict

  1. Convert a Dictionary Back Into a Data Class
  2. Convert a Dictionary Back Into a Data Class With __post_init__
  3. Convert a Dictionary Back Into a Data Class With Custom Implementation
  4. Convert a Dictionary Back Into a Data Class With dacite (Third Party Library)

The main aim of this article is to demonstrate how to convert a nested dictionary into a data class. Three different types of approaches are presented, along with their explanations.

Convert a Dictionary Back Into a Data Class

Depending on the project you are working on, you may encounter a scenario where you need to convert a dictionary back into a data class. While it may be a rare case, there are certain situations where you might need to do the conversion, especially when you want to prevent unexpected behavior.

An example of such a scenario would be that the converted dictionary is nested or complex, like having members with complex types or members who are data classes.

from dataclasses import dataclass, asdict

@dataclass
class A:
    x: int

@dataclass
class B:
    x: A
    y: A

@dataclass
class C:
    a: B
    b: B

In the above case, the data class C can sometimes pose conversion problems when converted into a dictionary. The problems occur primarily due to failed handling of types of class members.

To overcome this solution, you can choose several approaches based on your preference and the nature of the project. If you are working on a personal project with a good idea of the scope of the nature of the data, it is safe to use simple workarounds.

However, in the case of large-scaleable data, it is generally recommended to be safe and use third-party libraries (preferably open-source) since they are designed for operating on a large spectrum of use-cases. Thus, it allows them to handle a wider range of data variants.

Convert a Dictionary Back Into a Data Class With __post_init__

One of the easiest approaches is by taking advantage of the __post_init__ function called automatically after initializing the data class.

from dataclasses import dataclass, asdict

@dataclass
class SimpleDClazz:
    m_one: str
    m_two: str

@dataclass
class ComplexDClazz:
    simpKlazz: SimpleDClazz

    def __post_init__(self):
        if isinstance(self.simpKlazz, dict):
            self.simpKlazz = SimpleDClazz(**self.simpKlazz)

foo = ComplexDClazz(simpKlazz=SimpleDClazz(m_one="1", m_two="2"))

d= asdict(foo)
print(d)
o = ComplexDClazz(**d)
print(o)

Output:

{'simpKlazz': {'m_one': '1', 'm_two': '2'}}
ComplexDClazz(simpKlazz=SimpleDClazz(m_one='1', m_two='2'))

The __post_init_ function is called once the data class ComplexDClazz is initialized. Due to this unique property, we can use this to help convert the dictionary back to a data class.

All necessary implementations are done inside the __post_init_ function. The code can be broken down into two main parts:

  1. At the start of the function, it is determined whether the dictionary dict passed has the same type as the type of our member, simpKlazz.
  2. If the condition is true, the member variable simpKlazz is updated after converting the dictionary values to the correct type.

Suitability for Usage

Although small and independent, the solution holds well even after rigorous tests with different types of data classes. If you are looking for an independent and small solution, this solution seems like a good choice, but you may have to implement this function in every data class you plan to use.

If you are working on a large-scale project, apply your own test cases to the solution to see how suitable it is for your use cases.

Convert a Dictionary Back Into a Data Class With Custom Implementation

Consider the following function for converting a nested dictionary back to the data class.

import dataclasses as dc
from dataclasses import dataclass, asdict

def dict_to_dataklass(klass, d):
    try:
        fieldtypes = {f.name:f.type for f in dc.fields(klass)}
        return klass(**{f:dict_to_dataklass(fieldtypes[f],d[f]) for f in d})
    except:
        return d # The object is not a dataclass field


@dataclass
class SimpleDClazz:
    x: float
    y: float

@dataclass
class ComplexDClazz:
    a: SimpleDClazz
    b: SimpleDClazz

line = ComplexDClazz(SimpleDClazz(1,2), SimpleDClazz(3,4))
assert line == dict_to_dataklass(ComplexDClazz, asdict(line))
print("Success")

Output:

Success

This simple five-liner code consists of a try-catch block, in which two things are done:

  1. Assuming that klass is a data class. All the fields in the data class are extracted, their names and types to be more specific.
  2. After all the fields are retrieved, a data class object of type klass is generated from the retrieved fields. The recursive call ensures that any nested classes further inside the fields are handled and converted accordingly.
  3. When the try block fails (mostly when the passed parameters are not the required types), the original passed object is returned. This ensures that if the passed object is not of a suitable type, it is returned, acting as a stop condition for the recursive call.

Suitability for Usage

This method is best suited for smaller projects where the scope for vastly different types is not large, and most cases are known. As for projects of larger scale, this method may unexpectedly work when it encounters a corner case.

It is recommended to test this with test cases of your data class formats to root out any potential errors.

Convert a Dictionary Back Into a Data Class With dacite (Third Party Library)

dacite is an open-source, third-party library that aims to simplify the creation of data classes in Python. Luckily, the library consists of the function which does what we want, create a data class from a passed dictionary (nested or not).

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class DKlazz:
    m_one: str
    m_two: int
    is_working: bool

sample = {
    'm_one': '1',
    'm_two': 2,
    'is_working': False,
}

dk = from_dict(data_class=DKlazz, data=sample)

assert dk == DKlazz(m_one='1', m_two=2, is_working=False)
print("Success")

Output:

Success

dacite consists of only one function, from_dict, which allows the creation of a data class from a given dictionary object.

Using the function is fairly straightforward. It consists of two parameters: a data class and a dictionary.

The function then converts the given dictionary to the data class object of the given type and returns that—all without any hassle of custom functions.

Suitability for Usage

The good thing about open source is that in case any errors are encountered, they are immediately reported, and work on fixes starts momentarily. Keeping that in mind, this approach is all-around and can work for both small and large-scale projects.

If such cases are found, they can be reported to the GitHub repository, where the developer or other community members can help generate a fix. However, this option may not seem the most viable if you do not want to download any additional third-party library.

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Related Article - Python Dictionary

  • Check if a Key Exists in a Dictionary in Python
  • Convert a Dictionary to a List in Python
  • Get All the Files of a Directory
  • Find Maximum Value in Python Dictionary
  • Delete a File and Directory in Python
  • Sort a Python Dictionary by Value