Skip to content

FAQ

Why does d.items read as a method even when a key "items" exists?

Real dict attributes win on the attribute path. AttributeDict is designed so attribute access behaves like a plain dict for type attributes, while mapping access still returns key values:

d = AttributeDict(items=42)
d.items # <built-in method items...> — the real dict method
list(d.items) # [('items', 42)]
d["items"] # 42 — mapping access keeps the key's value
dict.items(d) # dict_items([('items', 42)]) — the mapping view

This is documented in attribute-semantics. The attribute path and the mapping path are intentionally asymmetric: attributes read type members; d[...] reads keys.

Why does del d.missing raise AttributeError but del d["missing"] raises KeyError?

The attribute form mirrors attribute semantics (AttributeError for a missing attribute); the mapping form mirrors mapping semantics (KeyError for a missing key). This deviation is documented in errors (spec 08).

Are non-identifier keys accessible as attributes?

No. Keys like "with-space", "123", "foo-bar", or "" are usable via mapping syntax only (d["with-space"]); attribute syntax raises AttributeError.

Is AttributeDict a real dict?

Yes — it's a C subclass of dict, so isinstance(d, dict) is True and it inherits the full mapping protocol and dict methods.

Is AttributeDict hashable?

No. Like dict, it's unhashable.

Which Python versions are supported?

CPython 3.9–3.14 via a single cp39-abi3 wheel per platform. PyPy and free-threaded CPython 3.13t aren't supported in v1.