Converting a Python Dictionary to an Object to Simplify Nested Data Structures — A Utility

When working with Python, it’s common to encounter dictionaries as a way to organize and store data. However, there are situations where accessing this data using object attributes rather than dictionary keys can make code cleaner, more intuitive, and easier to maintain.

Want to use Pydantic instead? See this article.

A Nested Data Structure Example

Imagine a dataset representing a library’s catalog of books. Each book includes information such as the title, author, and additional details like the publication year and genre. The data might look like this:

library_data = {
    "fiction": {
        "book1": {"title": "1984", "author": "George Orwell", "details": {"year": 1949, "genre": "Dystopian"}},
        "book2": {"title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "details": {"year": 1925, "genre": "Classic"}}
    },
    "non_fiction": {
        "book1": {"title": "Sapiens", "author": "Yuval Noah Harari", "details": {"year": 2011, "genre": "History"}},
        "book2": {"title": "Educated", "author": "Tara Westover", "details": {"year": 2018, "genre": "Memoir"}}
    }
}

Accessing the genre of “1984” in this structure would require:

genre = library_data["fiction"]["book1"]["details"]["genre"]

This approach works but becomes cumbersome as the structure grows deeper. By converting the dictionary into an object, the same data can be accessed like this:

genre = library.fiction.book1.details.genre

This change improves readability and makes the code more intuitive.

Why Convert a Dictionary to an Object?

  1. Improved Readability
    Accessing nested data through object attributes is more natural than chaining multiple keys in a dictionary. It reduces visual clutter and makes the intent of the code clearer.
  2. Consistency
    If an application already uses classes and objects for other components, converting dictionaries to objects keeps the code consistent and cohesive.
  3. Encapsulation
    Objects allow methods and behaviors to be attached to data. For example, a book object can include a method to format its details or perform specific validations.
  4. Interoperability
    Many libraries and frameworks are designed to work with objects. Converting a dictionary to an object can simplify integration with these tools.

The Code: Building the Conversion

To convert a dictionary into an object, we need a class that can recursively process the dictionary’s keys and values. Here’s a straightforward implementation:

class DictToObject:
    def __init__(self, dictionary):
        for key, value in dictionary.items():
            if isinstance(value, dict):
                # Recursively convert nested dictionaries
                setattr(self, key, DictToObject(value))
            else:
                setattr(self, key, value)

Let’s break this down:

  • Initialization: The __init__ method takes a dictionary as input.
  • Recursive Handling: For each key-value pair in the dictionary:
  • If the value is a dictionary, it creates another DictToObject instance.
  • If the value is not a dictionary, it assigns it as an attribute of the current object.

Applying the Conversion

Using the library data example, the process of converting and accessing the data becomes:

library = DictToObject(library_data)

# Access data as object attributes
print(library.fiction.book1.title)  # Output: 1984
print(library.non_fiction.book2.details.year)  # Output: 2018

This transformation allows seamless traversal of nested structures without dealing with square brackets and string keys.

Extending the Functionality

The base implementation of DictToObject handles most scenarios, but it can be extended to add more features:

Support for Lists
If the dictionary contains lists, these can be converted to lists of objects:

class DictToObject:
    def __init__(self, dictionary):
        for key, value in dictionary.items():
            if isinstance(value, dict):
                setattr(self, key, DictToObject(value))
            elif isinstance(value, list):
                setattr(self, key, [DictToObject(item) if isinstance(item, dict) else item for item in value])
            else:
                setattr(self, key, value)

Adding Methods
Methods can be included to provide specific functionality. For example, a method to print all book titles:

class Library(DictToObject):
    def get_all_titles(self):
        titles = []
        for category in self.__dict__.values():
            for book in category.__dict__.values():
                titles.append(book.title)
        return titles

Handling Edge Cases
Validations can ensure that only valid dictionaries are converted and that attributes don’t overwrite existing methods or properties.

When Not to Convert

While the conversion has benefits, it is not always necessary. Dictionaries are lightweight and flexible. If you only need to access a few keys and don’t have deeply nested structures, using a dictionary might be simpler. Moreover, dictionaries have built-in methods like keys() and values() that are not automatically available in objects unless explicitly implemented.


Thank you for reading this article. I hope you found it helpful and informative. If you have any questions, or if you would like to suggest new Python code examples or topics for future tutorials, please feel free to reach out. Your feedback and suggestions are always welcome!

Happy coding!
Py-Core.com Python Programming

You can also find this article at Medium.com

Leave a Reply