ChainMap.new_child / parents

Both return NEW ChainMaps that share the same underlying dicts. new_child puts a fresh (or given) mapping in front, so writes stay local; parents drops the front mapping — like leaving a scope.

ChainMap methodsPython 3.3+Live demo
Common call
local = scope.new_child() · outer = local.parents
Returns
new ChainMaps; the original is unchanged
Replaces
copying a whole dict to shadow a few keys
Watch out
parents of a one-map chain is ChainMap({}) — a new empty dict
ChainMap.new_child(mm — The mapping to put in front; None means a new empty dict (3.4+).type: mapping | None · default: None=None, **kwargs)
→ ChainMap

Demo

Live evaluation
An inner scope shadows the outer one; parents gets the outer view back.
Try:
Inputs
alist[str]outer names
blist[str]inner names
Code
from collections import ChainMap
outer = ChainMap(dict.fromkeys(['x', 'y'], 'outer'))
inner = outer.new_child(dict.fromkeys(['y', 'z'], 'inner'))
(dict(inner), inner.parents)
Result
({'x': 'outer', 'y': 'inner', 'z': 'inner'}, ChainMap({'x': 'outer', 'y': 'outer'}))

In "shadowing" y resolves to "inner" in the child, while inner.parents is the original outer chain with y still "outer". In the second tab the assignment goes into the new front dict, so parents shows the base mapping unchanged.

Parameters

NameTypeRequiredDescription
mmapping | Noneno (None)The mapping to put in front; None means a new empty dict (3.4+).
**kwargsvaluesnoKeys to set in the new front mapping (3.10+). With m given, they update m itself.

Return value

ChainMap — new_child: ChainMap(m, *self.maps). parents: ChainMap(*self.maps[1:]).

Common patterns

Enter and leave a scope
Rebind the name on entry and exit.
scope = scope.new_child()
try:
    run_block(scope)
finally:
    scope = scope.parents
Temporary overrides
Keyword arguments build the front mapping (3.10+).
debug_config = config.new_child(debug=True, log_level="DEBUG")
Skip the local scope on lookup
parents searches everything except maps[0].
outer_value = scope.parents[name]

Examples

1. new_child with no mapping
from collections import ChainMap ChainMap({'a': 1}).new_child()
Returns
ChainMap({}, {'a': 1})
2. new_child with keyword values
from collections import ChainMap ChainMap({'a': 1}).new_child(a=2)['a']
Returns
2
3. parents drops maps[0]
from collections import ChainMap ChainMap({'a': 1}, {'b': 2}).parents
Returns
ChainMap({'b': 2})
4. parents of a single map
from collections import ChainMap ChainMap({'a': 1}).parents
Returns
ChainMap({})
5. The original is not changed
from collections import ChainMap base = ChainMap({'a': 1}) child = base.new_child({'a': 2}) (base['a'], child['a'])
Returns
(1, 2)
6. Mappings are shared
from collections import ChainMap d = {'a': 1} ChainMap(d).new_child().maps[1] is d
Returns
True

Pitfalls

1. Calling parents like a method
parents is a property.
cm.parents()
from collections import ChainMap
ChainMap({}, {'a': 1}).parents()
TypeError: 'ChainMap' object is not callable
cm.parents
from collections import ChainMap
ChainMap({}, {'a': 1}).parents
ChainMap({'a': 1})
2. Forgetting to rebind
new_child returns a new ChainMap; the old variable still points at the old chain.
call only
from collections import ChainMap
cm = ChainMap({'a': 1})
cm.new_child({'a': 2})
cm['a']
1
cm = cm.new_child(...)
from collections import ChainMap
cm = ChainMap({'a': 1})
cm = cm.new_child({'a': 2})
cm['a']
2

When to use

Use it
  • Interpreters, template engines and config systems with nested scopes
  • Trying out overrides without touching the base settings
Reach for something else
  • Deeply nested chains on hot paths → lookups get slower with every layer

Notes

CPython impl
new_child returns self.__class__(m, *self.maps); parents returns self.__class__(*self.maps[1:])
Versions
m parameter 3.4, keyword arguments 3.10 (docs.python.org)

FAQ

It returns a new ChainMap with a mapping in front of the existing ones: a new empty dict by default, or the mapping you pass. Lookups see the new mapping first and all writes go into it.