The field() function allows you to customize how individual class attributes are serialized and deserialized. Use it as a default value in your __init__ method.
Basic Usage
from lodum import lodum, field, json
@lodum
class User:
def __init__(
self,
email: str,
user_id: int = field(rename="id", default=0),
password_hash: str = field(skip_serializing=True, default=""),
preferences: dict = field(default_factory=dict),
):
self.email = email
self.user_id = user_id
self.password_hash = password_hash
self.preferences = preferences
user = User(email="[email protected]", user_id=123, password_hash="secret")
print(json.dumps(user))
# {"email": "[email protected]", "id": 123, "preferences": {}}
# Note: password_hash is excluded, user_id becomes "id"
Field Options
rename
Change the field name in the serialized output:
from lodum import lodum, field, json
@lodum
class Product:
def __init__(
self,
product_id: int = field(rename="id"),
product_name: str = field(rename="name"),
):
self.product_id = product_id
self.product_name = product_name
product = Product(product_id=42, product_name="Widget")
print(json.dumps(product))
# {"id": 42, "name": "Widget"}
# Deserialization uses the renamed field
product = json.loads(Product, '{"id": 99, "name": "Gadget"}')
print(product.product_id) # 99
skip_serializing
Exclude a field from serialization output:
from lodum import lodum, field, json
@lodum
class Account:
def __init__(
self,
username: str,
password: str = field(skip_serializing=True, default=""),
api_key: str = field(skip_serializing=True, default=""),
):
self.username = username
self.password = password
self.api_key = api_key
account = Account(username="alice", password="secret123", api_key="key_abc")
print(json.dumps(account))
# {"username": "alice"}
# Sensitive fields are excluded
Fields with skip_serializing=True are not included in the output, but can still be deserialized if present in the input. For security-critical use cases, ensure sensitive data is never sent to the client.
default
Provide a default value for missing fields during deserialization:
from lodum import lodum, field, json
@lodum
class Settings:
def __init__(
self,
theme: str = field(default="light"),
timeout: int = field(default=30),
debug: bool = field(default=False),
):
self.theme = theme
self.timeout = timeout
self.debug = debug
# Missing fields use defaults
settings = json.loads(Settings, '{"theme": "dark"}')
print(settings.timeout) # 30
print(settings.debug) # False
default_factory
Use a callable to generate default values (important for mutable defaults):
from lodum import lodum, field, json
@lodum
class Task:
def __init__(
self,
name: str,
tags: list[str] = field(default_factory=list),
metadata: dict = field(default_factory=dict),
):
self.name = name
self.tags = tags
self.metadata = metadata
task1 = Task(name="Task 1")
task2 = Task(name="Task 2")
task1.tags.append("urgent")
print(task2.tags) # [] - separate list instances
Always use default_factory for mutable defaults like list, dict, or set to avoid sharing instances between objects.
serializer
Custom function to transform values during serialization:
from lodum import lodum, field, json
from datetime import datetime
@lodum
class Event:
def __init__(
self,
name: str,
created_at: datetime = field(
serializer=lambda dt: dt.isoformat(),
default_factory=datetime.now,
),
):
self.name = name
self.created_at = created_at
event = Event(name="Launch", created_at=datetime(2026, 3, 3, 12, 0, 0))
print(json.dumps(event))
# {"name": "Launch", "created_at": "2026-03-03T12:00:00"}
Lodum has built-in support for datetime.datetime, so this example is illustrative. The library automatically converts datetimes to ISO 8601 strings.
deserializer
Custom function to transform values during deserialization:
from lodum import lodum, field, json
from datetime import datetime
@lodum
class Event:
def __init__(
self,
name: str,
created_at: datetime = field(
serializer=lambda dt: dt.isoformat(),
deserializer=lambda s: datetime.fromisoformat(s),
),
):
self.name = name
self.created_at = created_at
event_data = '{"name": "Launch", "created_at": "2026-03-03T12:00:00"}'
event = json.loads(Event, event_data)
print(type(event.created_at)) # <class 'datetime.datetime'>
print(event.created_at.year) # 2026
validate
Add validation functions to ensure data integrity. See the Validation System guide for details.
from lodum import lodum, field, json
def positive(value):
if value <= 0:
raise ValueError("Must be positive")
@lodum
class Product:
def __init__(
self,
name: str,
price: float = field(validate=positive),
):
self.name = name
self.price = price
# This raises DeserializationError
try:
json.loads(Product, '{"name": "Widget", "price": -10}')
except Exception as e:
print(e) # Error at price: Must be positive
Field Type Signature
From src/lodum/field.py:107-143:
def field(
*,
rename: Optional[str] = None,
skip_serializing: bool = False,
default: Any = _MISSING,
default_factory: Optional[Callable[[], Any]] = None,
serializer: Optional[Callable[[Any], Any]] = None,
deserializer: Optional[Callable[[Any], Any]] = None,
validate: Optional[
Union[Callable[[Any], None], List[Callable[[Any], None]]]
] = None,
) -> Any:
"""
Provides metadata to the `@lodum` decorator for a single field.
Args:
rename: The name to use for the field in the output.
skip_serializing: If `True`, the field will not be included in the
output.
default: A default value to use for the field during decoding
if it is missing from the input data.
default_factory: A zero-argument function that will be called to
create a default value for a missing field.
serializer: A function to call to encode the field's value.
deserializer: A function to call to decode the field's value.
validate: A callable or list of callables to validate the field's value during decoding.
"""
The Field Class
Internally, field() returns a Field instance from src/lodum/field.py:27-105:
class Field:
"""
A class that stores metadata for a field in a lodum-enabled class.
This is not intended to be instantiated directly. Instead, use the `field()`
function, which provides a more convenient API.
"""
def __init__(
self,
rename: Optional[str] = None,
skip_serializing: bool = False,
default: Any = _MISSING,
default_factory: Optional[Callable[[], Any]] = None,
serializer: Optional[Callable[[Any], Any]] = None,
deserializer: Optional[Callable[[Any], Any]] = None,
validate: Optional[
Union[Callable[[Any], None], List[Callable[[Any], None]]]
] = None,
) -> None:
if default is not _MISSING and default_factory is not None:
raise ValueError("cannot specify both default and default_factory")
self.rename = rename
self.skip_serializing = skip_serializing
self.default = default
self.default_factory = default_factory
self.serializer = serializer
self.deserializer = deserializer
self.validate = validate
self.name: str = "" # Will be populated by the decorator
self.type: Any = None # Will be populated by the decorator
@property
def has_default(self) -> bool:
return self.default is not _MISSING or self.default_factory is not None
def get_default(self) -> Any:
if self.default_factory is not None:
return self.default_factory()
return self.default
Advanced Example
Combine multiple field options:
from lodum import lodum, field, json
from lodum.validators import Length, Range
from datetime import datetime
@lodum
class BlogPost:
def __init__(
self,
# Renamed field with validation
post_id: int = field(rename="id", validate=Range(min=1)),
# Required field with length validation
title: str = field(validate=Length(min=1, max=200)),
# Field with custom serializer/deserializer
published_at: datetime = field(
rename="published",
serializer=lambda dt: dt.isoformat(),
deserializer=lambda s: datetime.fromisoformat(s),
),
# Optional field with factory default
tags: list[str] = field(default_factory=list),
# Private field excluded from output
edit_token: str = field(skip_serializing=True, default=""),
):
self.post_id = post_id
self.title = title
self.published_at = published_at
self.tags = tags
self.edit_token = edit_token
post = BlogPost(
post_id=1,
title="Hello Lodum",
published_at=datetime(2026, 3, 3),
tags=["python", "serialization"],
edit_token="secret_abc123",
)
print(json.dumps(post, indent=2))
# {
# "id": 1,
# "title": "Hello Lodum",
# "published": "2026-03-03T00:00:00",
# "tags": ["python", "serialization"]
# }
Field Parameters Summary
| Parameter | Type | Default | Description |
|---|
rename | str | None | None | Serialized field name |
skip_serializing | bool | False | Exclude from output |
default | Any | _MISSING | Default value if missing |
default_factory | Callable[[], Any] | None | None | Function to create default |
serializer | Callable[[Any], Any] | None | None | Custom encode function |
deserializer | Callable[[Any], Any] | None | None | Custom decode function |
validate | Callable | List[Callable] | None | None | Validation function(s) |
Constraints
Cannot specify both default and default_factory. The Field constructor will raise ValueError if you try.
# This raises ValueError
field(default=[], default_factory=list) # Error!
# Use one or the other
field(default=[]) # OK for immutable
field(default_factory=list) # OK for mutable