Sometimes you need a simple object that groups values behind attribute access without defining a full class. types.SimpleNamespace provides exactly that: a mutable container backed by __dict__, constructed from keyword arguments, with a readable representation and equality based on stored attributes.
This guide explains how to create namespaces, convert dictionaries, add and remove attributes, compare instances, copy objects, handle nested data, integrate with JSON and argparse, and decide when a dataclass, TypedDict, NamedTuple, or regular class is more appropriate.
Your first SimpleNamespace
from types import SimpleNamespace
user = SimpleNamespace(name="Ana", active=True)
print(user.name)
print(user.active)
Keyword arguments are inserted into the instance __dict__. There is no field declaration, runtime validation, or automatic conversion.
Adding attributes later
user.id = 42
user.email = "ana@example.com"
The object is dynamic and mutable. This is convenient for prototypes and temporary state, but a misspelled name may silently create a new attribute.
Building from a dictionary
data = {"host": "localhost", "port": 8000}
config = SimpleNamespace(**data)
print(config.port)
Keys must be strings accepted as keyword names. Keys containing hyphens, spaces, or non-string values cannot be expanded directly with **.
Converting back to a dictionary
mapping = vars(config)
vars(config) returns the actual attribute dictionary, not a copy. Mutating it changes the namespace.
snapshot = vars(config).copy()
Create a copy when independent state is required.
Readable representation
print(SimpleNamespace(x=1, y=2))
# namespace(x=1, y=2)
The representation is helpful in tests and debugging, but it may display sensitive values. Redact passwords, tokens, and personal data before logging.
Equality
a = SimpleNamespace(x=1, y=2)
b = SimpleNamespace(y=2, x=1)
print(a == b) # True
Equality compares attribute dictionaries. Insertion order does not matter. The class is mutable and not intended as a hashable dictionary key or set member.
Removing attributes
del user.email
Accessing the deleted attribute raises AttributeError. Use hasattr() or getattr(object, name, default) for optional data.
Dynamic getattr and setattr
field_name = "timeout"
setattr(config, field_name, 30)
value = getattr(config, field_name)
These functions are useful when names come from metadata. Validate an allowlist before accepting external field names, especially when callers could overwrite attributes with special meaning.
Nested namespaces
app = SimpleNamespace(
database=SimpleNamespace(host="db", port=5432),
debug=False,
)
Nested dictionaries are not converted automatically. Build nested namespaces explicitly when dot access is desirable.
Recursive conversion
def to_namespace(value):
if isinstance(value, dict):
return SimpleNamespace(
**{key: to_namespace(item) for key, item in value.items()}
)
if isinstance(value, list):
return [to_namespace(item) for item in value]
return value
Before applying this helper to external data, verify that every key is a suitable identifier and that no unexpected special names are accepted.
JSON serialization
The default JSON encoder does not serialize SimpleNamespace directly. A small object can be converted with:
import json
text = json.dumps(config, default=vars)
default=vars may also expose attributes of other objects with __dict__. Explicit conversion is safer for public APIs.
Shallow and deep copies
from copy import copy
clone = copy(app)
A shallow copy creates another namespace while nested mutable values remain shared. Use deepcopy() only when the graph supports it and the extra cost is justified.
Using it with argparse
ArgumentParser.parse_args() returns a namespace-like object and can populate an existing instance:
from argparse import ArgumentParser
from types import SimpleNamespace
parser = ArgumentParser()
parser.add_argument("--port", type=int, default=8000)
config = parser.parse_args(namespace=SimpleNamespace())
For larger applications, validate and convert the parsed result to an explicit model before startup.
Prototyping and test fixtures
SimpleNamespace is effective for quick tests, stubs, fixtures, and internal return values when a formal class would add unnecessary ceremony. Attribute access can make result.value clearer than tuple indexes.
It is not a schema
The object does not declare required fields, types, defaults, or documentation. Static analyzers have little information about dynamic attributes. A dataclass or TypedDict gives a stronger long-lived contract.
SimpleNamespace versus dataclass
from dataclasses import dataclass
@dataclass
class Config:
host: str
port: int = 8000
Dataclasses provide explicit fields, type hints, predictable construction, immutability options, and better IDE support. Use SimpleNamespace when the shape is temporary or genuinely dynamic.
Versus TypedDict
TypedDict describes dictionaries accessed by keys and primarily supports static analysis. SimpleNamespace provides runtime attribute access. Match the abstraction to the real data format rather than converting only for stylistic preference.
Versus NamedTuple
NamedTuple is immutable, indexable, hashable, and has fixed fields. SimpleNamespace is mutable, not index-oriented, and accepts new attributes. NamedTuple is often better for stable lightweight records.
Versus a regular class
A normal class supports invariants, properties, methods, validation, and encapsulation. Once a temporary object gains behavior or becomes part of a public API, migrate it to an explicit class.
Providing defaults
SimpleNamespace has no field declaration for defaults. Use a factory:
def new_config(**overrides):
values = {"host": "localhost", "port": 8000, "debug": False}
unknown = set(overrides) - set(values)
if unknown:
raise TypeError(f"unknown options: {sorted(unknown)}")
values.update(overrides)
return SimpleNamespace(**values)
Checking unknown names prevents silent typos.
Calculated fields
You can store a calculated value, but it does not update automatically when dependencies change. Use a class property when a value should always be derived from current state.
Subclassing
SimpleNamespace can be subclassed, but once methods, validation, and fixed structure are needed, a regular class or dataclass usually communicates intent more clearly.
Security with external data
Do not convert arbitrary JSON to attributes and then use those attributes to control imports, filesystem paths, queries, or function calls without validation. Dot notation does not make input trusted.
Thread safety
The namespace provides no synchronization. Multiple threads mutating attributes require the same locking discipline as a shared dictionary. Prefer immutable snapshots for configuration read by many workers.
Common mistakes
- Treating it as a validated model: any attribute can be created.
- Using vars as an independent mapping: it returns the live dictionary.
- Expecting recursive conversion: nested dictionaries remain dictionaries.
- Publishing it as a stable API contract: fields are not declared.
- Logging secrets: the repr includes attributes.
- Assuming a shallow copy is isolated: nested objects remain shared.
Complete processing-result example
from types import SimpleNamespace
def process(lines):
errors = []
valid = []
for number, line in enumerate(lines, 1):
try:
valid.append(normalize(line))
except ValueError as error:
errors.append((number, str(error)))
return SimpleNamespace(
total=len(lines),
valid=valid,
errors=errors,
success=not errors,
)
result = process(lines)
if result.success:
save(result.valid)
The namespace works well as a simple internal result. If the result becomes part of a library’s public interface, a typed dataclass offers a clearer contract.
Conclusion
types.SimpleNamespace is a lightweight container for values accessed through attributes. It reduces boilerplate in prototypes, tests, and temporary structures while providing useful representation and equality behavior.
The official Python SimpleNamespace documentation defines the class. Use it for simple dynamic data and move to a dataclass, TypedDict, NamedTuple, or regular class when schema, validation, or behavior becomes important.







