Overview
asdict() and fromdict() provide a simple API for converting lodum-enabled objects to and from plain Python data structures (dictionaries, lists, primitives). These functions are useful when you need to work with raw data structures without committing to a specific serialization format.
asdict()
Recursively converts a lodum-enabled object into plain Python primitives (dict, list, etc.).
Signature
def asdict(obj: Any) -> Any
Parameters
The object to convert. Can be a lodum-enabled class instance, a collection containing lodum objects, or any supported type.
Returns
Plain Python data structures:
- Lodum objects →
dict
- Lists/sequences →
list
- Dictionaries →
dict
- Primitives → unchanged (
int, str, float, bool, None)
- Enums → their
.value
- Datetimes → ISO 8601 strings
- Other types → format-specific representations
Examples
Basic Usage
from lodum import lodum, asdict
@lodum
class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
user = User(name="Alice", age=30)
data = asdict(user)
# {'name': 'Alice', 'age': 30}
Nested Objects
from lodum import lodum, asdict
from typing import List
@lodum
class Address:
def __init__(self, street: str, city: str):
self.street = street
self.city = city
@lodum
class Person:
def __init__(self, name: str, address: Address):
self.name = name
self.address = address
person = Person(
name="Bob",
address=Address(street="123 Main St", city="Boston")
)
data = asdict(person)
# {
# 'name': 'Bob',
# 'address': {
# 'street': '123 Main St',
# 'city': 'Boston'
# }
# }
With Field Renaming
from lodum import lodum, field, asdict
@lodum
class Config:
def __init__(
self,
api_key: str = field(rename="apiKey"),
max_retries: int = field(rename="maxRetries"),
):
self.api_key = api_key
self.max_retries = max_retries
config = Config(api_key="secret", max_retries=3)
data = asdict(config)
# {'apiKey': 'secret', 'maxRetries': 3}
With Collections
from lodum import lodum, asdict
from typing import List
@lodum
class Item:
def __init__(self, id: int, name: str):
self.id = id
self.name = name
items = [
Item(id=1, name="First"),
Item(id=2, name="Second"),
]
data = asdict(items)
# [
# {'id': 1, 'name': 'First'},
# {'id': 2, 'name': 'Second'}
# ]
fromdict()
Hydrates a lodum-enabled class from a dictionary or other plain Python primitives.
Signature
def fromdict(cls: Type[T], data: Any) -> T
Parameters
The lodum-enabled class to instantiate. Must be decorated with @lodum.
The data to deserialize. Typically a dictionary, but can be any supported primitive or collection.
Returns
An instance of the specified class T, fully hydrated with the provided data.
Raises
DeserializationError - If the data doesn’t match the expected type or structure
ValueError - If validation fails (when using field(validate=...))
Examples
Basic Usage
from lodum import lodum, fromdict
@lodum
class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
data = {'name': 'Alice', 'age': 30}
user = fromdict(User, data)
# User(name='Alice', age=30)
Nested Objects
from lodum import lodum, fromdict
from typing import List
@lodum
class Child:
def __init__(self, name: str):
self.name = name
@lodum
class Parent:
def __init__(self, name: str, children: List[Child]):
self.name = name
self.children = children
data = {
'name': 'Alice',
'children': [
{'name': 'Bob'},
{'name': 'Charlie'}
]
}
parent = fromdict(Parent, data)
# Parent with 2 Child instances
assert parent.children[0].name == 'Bob'
assert isinstance(parent.children[0], Child)
With Renamed Fields
from lodum import lodum, field, fromdict
@lodum
class Config:
def __init__(
self,
api_key: str = field(rename="apiKey"),
max_retries: int = field(rename="maxRetries"),
):
self.api_key = api_key
self.max_retries = max_retries
data = {'apiKey': 'secret', 'maxRetries': 3}
config = fromdict(Config, data)
# Config(api_key='secret', max_retries=3)
With Defaults
from lodum import lodum, field, fromdict
@lodum
class Settings:
def __init__(
self,
host: str,
port: int = field(default=8080),
):
self.host = host
self.port = port
# Port is optional in the data
data = {'host': 'localhost'}
settings = fromdict(Settings, data)
# Settings(host='localhost', port=8080)
With Validation
from lodum import lodum, field, fromdict
from lodum.exception import DeserializationError
def validate_positive(value: int) -> None:
if value <= 0:
raise ValueError("Must be positive")
@lodum
class Product:
def __init__(
self,
price: float = field(validate=validate_positive),
):
self.price = price
# Valid data
product = fromdict(Product, {'price': 10.0})
# Product(price=10.0)
# Invalid data raises during deserialization
try:
fromdict(Product, {'price': -5.0})
except ValueError as e:
print(e) # "Must be positive"
Round-Trip Conversion
These functions are designed to work together:
from lodum import lodum, asdict, fromdict
from typing import List
@lodum
class Note:
def __init__(self, title: str, tags: List[str]):
self.title = title
self.tags = tags
original = Note(title="Meeting", tags=["work", "important"])
# Convert to dict
data = asdict(original)
# Convert back to object
restored = fromdict(Note, data)
assert restored.title == original.title
assert restored.tags == original.tags
Type Safety
Both functions perform full type validation:
from lodum import lodum, fromdict
from lodum.exception import DeserializationError
@lodum
class Typed:
def __init__(self, count: int):
self.count = count
# This will raise DeserializationError
try:
fromdict(Typed, {'count': 'not-a-number'})
except DeserializationError as e:
print(e) # "Expected int, got str"
Working with Collections
from lodum import lodum, asdict
import collections
@lodum
class Item:
def __init__(self, value: int):
self.value = value
# Works with UserList, deque, etc.
my_list = collections.deque([Item(1), Item(2)])
data = asdict(my_list)
# [{'value': 1}, {'value': 2}] # Plain list
assert type(data) is list
See Also