SimpleNamespace: Lightweight Attribute Objects

Published on: August 29, 2026
Reading time: 5 minutes
Close-up view of a computer screen displaying code in a software development environment.

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.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Folders and directories for Python contextlib.chdir
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    contextlib.chdir: Restore Directories Automatically

    Learn Python contextlib.chdir to change directories temporarily, restore paths safely, isolate tests, and avoid global-state bugs.

    Ler mais

    Tempo de leitura: 5 minutos
    03/09/2026
    Python code execution and performance monitoring
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    sys.monitoring: Low-Overhead Instrumentation

    Learn Python sys.monitoring for low-overhead instrumentation with selective events, callbacks, tooling, and safe observability.

    Ler mais

    Tempo de leitura: 5 minutos
    03/09/2026
    Software developer organizing object data with Python operator.attrgetter
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    operator.attrgetter: Sort Objects by Attributes

    Learn Python operator.attrgetter to sort, group, and transform objects by simple or nested attributes with clearer reusable code.

    Ler mais

    Tempo de leitura: 4 minutos
    02/09/2026
    Asynchronous programming with Python asyncio.Runner
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Runner: Reuse the Event Loop Safely

    Learn Python asyncio.Runner to reuse an event loop, control context, signals, debug mode, cancellation, and safe asynchronous shutdown.

    Ler mais

    Tempo de leitura: 6 minutos
    02/09/2026
    Binary data compression with Zstandard in Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard Streams and Dictionaries

    Learn Python compression.zstd for Zstandard compression, streaming, dictionaries, safe limits, testing, and production workflows.

    Ler mais

    Tempo de leitura: 6 minutos
    01/09/2026
    Python application packaged as an executable zipapp archive
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python zipapp: Build Executable Apps

    Learn Python zipapp to package applications as executable pyz archives, include dependencies, and distribute tools safely.

    Ler mais

    Tempo de leitura: 5 minutos
    01/09/2026