Standard dictionaries and lists handle most cases, but counting frequencies, grouping items, or maintaining queues require repetitive boilerplate. The collections module provides specialized containers that handle these patterns efficiently with clean, readable code.

ChainMap

Combine multiple dictionaries:

counter.py
# Counter examples

from collections import Counter

# Basic Counter
print("Basic Counter:")

# Count from list
items = 
counter = Counter(items)

print(f"Items: {items}")
print(f"Counter: {counter}")
print(f"apple: {counter['apple']}")
print(f"banana: {counter['banana']}")
print(f"grape: {counter['grape']}")  # Returns 0, not KeyError

# Count from string
print("\nCount from string:")

text = "hello world"
char_count = Counter(text)

print(f"Text: '{text}'")
print(f"Characters: {char_count}")
print(f"'l': {char_count['l']}")
print(f"'o': {char_count['o']}")

# Most common
print("\nMost common:")

words = ['the', 'quick', 'brown', 'fox', 'the', 'lazy', 'dog', 'the', 'quick']
word_count = Counter(words)

print(f"Words: {words}")
print(f"Most common 3: {word_count.most_common(3)}")
print(f"All by frequency: {word_count.most_common()}")

# Count votes
print("\nCount votes:")

votes = ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice', 'Bob', 'Alice']
results = Counter(votes)

print(f"Votes: {votes}")
for candidate, count in results.most_common():
    print(f"  {candidate}: {count} votes")

winner = results.most_common(1)[0][0]
print(f"Winner: {winner}")

# Update counter
print("\nUpdate counter:")

c1 = Counter(['a', 'b', 'c'])
print(f"Initial: {c1}")

c1.update(['a', 'b', 'd'])
print(f"After update: {c1}")

c1.update({'a': 2, 'e': 3})
print(f"After dict update: {c1}")

# Subtract
print("\nSubtract:")

inventory = Counter(apples=10, bananas=5, oranges=8)
sold = Counter(apples=3, bananas=2, oranges=1)

print(f"Inventory: {inventory}")
print(f"Sold: {sold}")

inventory.subtract(sold)
print(f"Remaining: {inventory}")

# Counter arithmetic
print("\nCounter arithmetic:")

c1 = Counter(a=3, b=2, c=1)
c2 = Counter(a=1, b=2, d=3)

print(f"C1: {c1}")
print(f"C2: {c2}")
print(f"C1 + C2: {c1 + c2}")
print(f"C1 - C2: {c1 - c2}")
print(f"C1 & C2 (intersection): {c1 & c2}")
print(f"C1 | C2 (union): {c1 | c2}")

# Elements
print("\nElements:")

c = Counter(a=3, b=2, c=1)
print(f"Counter: {c}")
print(f"Elements: {list(c.elements())}")

# Sorted
print(f"Sorted: {sorted(c.elements())}")

# Total count
print("\nTotal count:")

inventory = Counter(apples=10, bananas=5, oranges=8)
print(f"Inventory: {inventory}")
print(f"Total items: {sum(inventory.values())}")

# Or use total() in Python 3.10+
# print(f"Total: {inventory.total()}")

# Most common letter
print("\nMost common letter:")

text = "the quick brown fox jumps over the lazy dog"
letters = Counter(c for c in text.lower() if c.isalpha())

print(f"Text: '{text}'")
print(f"Most common 5 letters: {letters.most_common(5)}")

# Word frequency
print("\nWord frequency:")

document = "the cat and the dog and the bird"
words = document.split()
freq = Counter(words)

print(f"Document: '{document}'")
for word, count in sorted(freq.items(), key=lambda x: x[1], reverse=True):
    print(f"  '{word}': {count}")

# Clear counter
print("\nClear counter:")

c = Counter(a=1, b=2, c=3)
print(f"Before: {c}")

c.clear()
print(f"After clear: {c}")

# Counter examples

from collections import Counter

# Basic Counter
print("Basic Counter:")

# Count from list
items = 
counter = Counter(items)

print(f"Items: {items}")
print(f"Counter: {counter}")
print(f"apple: {counter['apple']}")
print(f"banana: {counter['banana']}")
print(f"grape: {counter['grape']}")  # Returns 0, not KeyError

# Count from string
print("\nCount from string:")

text = "hello world"
char_count = Counter(text)

print(f"Text: '{text}'")
print(f"Characters: {char_count}")
print(f"'l': {char_count['l']}")
print(f"'o': {char_count['o']}")

# Most common
print("\nMost common:")

words = ['the', 'quick', 'brown', 'fox', 'the', 'lazy', 'dog', 'the', 'quick']
word_count = Counter(words)

print(f"Words: {words}")
print(f"Most common 3: {word_count.most_common(3)}")
print(f"All by frequency: {word_count.most_common()}")

# Count votes
print("\nCount votes:")

votes = ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice', 'Bob', 'Alice']
results = Counter(votes)

print(f"Votes: {votes}")
for candidate, count in results.most_common():
    print(f"  {candidate}: {count} votes")

winner = results.most_common(1)[0][0]
print(f"Winner: {winner}")

# Update counter
print("\nUpdate counter:")

c1 = Counter(['a', 'b', 'c'])
print(f"Initial: {c1}")

c1.update(['a', 'b', 'd'])
print(f"After update: {c1}")

c1.update({'a': 2, 'e': 3})
print(f"After dict update: {c1}")

# Subtract
print("\nSubtract:")

inventory = Counter(apples=10, bananas=5, oranges=8)
sold = Counter(apples=3, bananas=2, oranges=1)

print(f"Inventory: {inventory}")
print(f"Sold: {sold}")

inventory.subtract(sold)
print(f"Remaining: {inventory}")

# Counter arithmetic
print("\nCounter arithmetic:")

c1 = Counter(a=3, b=2, c=1)
c2 = Counter(a=1, b=2, d=3)

print(f"C1: {c1}")
print(f"C2: {c2}")
print(f"C1 + C2: {c1 + c2}")
print(f"C1 - C2: {c1 - c2}")
print(f"C1 & C2 (intersection): {c1 & c2}")
print(f"C1 | C2 (union): {c1 | c2}")

# Elements
print("\nElements:")

c = Counter(a=3, b=2, c=1)
print(f"Counter: {c}")
print(f"Elements: {list(c.elements())}")

# Sorted
print(f"Sorted: {sorted(c.elements())}")

# Total count
print("\nTotal count:")

inventory = Counter(apples=10, bananas=5, oranges=8)
print(f"Inventory: {inventory}")
print(f"Total items: {sum(inventory.values())}")

# Or use total() in Python 3.10+
# print(f"Total: {inventory.total()}")

# Most common letter
print("\nMost common letter:")

text = "the quick brown fox jumps over the lazy dog"
letters = Counter(c for c in text.lower() if c.isalpha())

print(f"Text: '{text}'")
print(f"Most common 5 letters: {letters.most_common(5)}")

# Word frequency
print("\nWord frequency:")

document = "the cat and the dog and the bird"
words = document.split()
freq = Counter(words)

print(f"Document: '{document}'")
for word, count in sorted(freq.items(), key=lambda x: x[1], reverse=True):
    print(f"  '{word}': {count}")

# Clear counter
print("\nClear counter:")

c = Counter(a=1, b=2, c=3)
print(f"Before: {c}")

c.clear()
print(f"After clear: {c}")

# Counter examples

from collections import Counter

# Basic Counter
print("Basic Counter:")

# Count from list
items = 
counter = Counter(items)

print(f"Items: {items}")
print(f"Counter: {counter}")
print(f"apple: {counter['apple']}")
print(f"banana: {counter['banana']}")
print(f"grape: {counter['grape']}")  # Returns 0, not KeyError

# Count from string
print("\nCount from string:")

text = "hello world"
char_count = Counter(text)

print(f"Text: '{text}'")
print(f"Characters: {char_count}")
print(f"'l': {char_count['l']}")
print(f"'o': {char_count['o']}")

# Most common
print("\nMost common:")

words = ['the', 'quick', 'brown', 'fox', 'the', 'lazy', 'dog', 'the', 'quick']
word_count = Counter(words)

print(f"Words: {words}")
print(f"Most common 3: {word_count.most_common(3)}")
print(f"All by frequency: {word_count.most_common()}")

# Count votes
print("\nCount votes:")

votes = ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice', 'Bob', 'Alice']
results = Counter(votes)

print(f"Votes: {votes}")
for candidate, count in results.most_common():
    print(f"  {candidate}: {count} votes")

winner = results.most_common(1)[0][0]
print(f"Winner: {winner}")

# Update counter
print("\nUpdate counter:")

c1 = Counter(['a', 'b', 'c'])
print(f"Initial: {c1}")

c1.update(['a', 'b', 'd'])
print(f"After update: {c1}")

c1.update({'a': 2, 'e': 3})
print(f"After dict update: {c1}")

# Subtract
print("\nSubtract:")

inventory = Counter(apples=10, bananas=5, oranges=8)
sold = Counter(apples=3, bananas=2, oranges=1)

print(f"Inventory: {inventory}")
print(f"Sold: {sold}")

inventory.subtract(sold)
print(f"Remaining: {inventory}")

# Counter arithmetic
print("\nCounter arithmetic:")

c1 = Counter(a=3, b=2, c=1)
c2 = Counter(a=1, b=2, d=3)

print(f"C1: {c1}")
print(f"C2: {c2}")
print(f"C1 + C2: {c1 + c2}")
print(f"C1 - C2: {c1 - c2}")
print(f"C1 & C2 (intersection): {c1 & c2}")
print(f"C1 | C2 (union): {c1 | c2}")

# Elements
print("\nElements:")

c = Counter(a=3, b=2, c=1)
print(f"Counter: {c}")
print(f"Elements: {list(c.elements())}")

# Sorted
print(f"Sorted: {sorted(c.elements())}")

# Total count
print("\nTotal count:")

inventory = Counter(apples=10, bananas=5, oranges=8)
print(f"Inventory: {inventory}")
print(f"Total items: {sum(inventory.values())}")

# Or use total() in Python 3.10+
# print(f"Total: {inventory.total()}")

# Most common letter
print("\nMost common letter:")

text = "the quick brown fox jumps over the lazy dog"
letters = Counter(c for c in text.lower() if c.isalpha())

print(f"Text: '{text}'")
print(f"Most common 5 letters: {letters.most_common(5)}")

# Word frequency
print("\nWord frequency:")

document = "the cat and the dog and the bird"
words = document.split()
freq = Counter(words)

print(f"Document: '{document}'")
for word, count in sorted(freq.items(), key=lambda x: x[1], reverse=True):
    print(f"  '{word}': {count}")

# Clear counter
print("\nClear counter:")

c = Counter(a=1, b=2, c=3)
print(f"Before: {c}")

c.clear()
print(f"After clear: {c}")

defaultdict.py
# defaultdict examples

from collections import defaultdict

# Basic defaultdict
print("Basic defaultdict:")

# With list
dd = defaultdict(list)

dd['fruits'].append('apple')
dd['fruits'].append('banana')
dd['vegetables'].append('carrot')

print(f"DefaultDict: {dict(dd)}")
print(f"Fruits: {dd['fruits']}")
print(f"Non-existent: {dd['meats']}")  # Returns []

# With int (counter)
print("\nWith int (counter):")

counts = defaultdict(int)

words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
for word in words:
    counts[word] += 1

print(f"Words: {words}")
print(f"Counts: {dict(counts)}")

# With set
print("\nWith set:")

groups = defaultdict(set)

data = [
    ('Alice', 'Math'),
    ('Bob', 'Science'),
    ('Alice', 'English'),
    ('Charlie', 'Math'),
    ('Bob', 'Math')
]

for student, subject in data:
    groups[student].add(subject)

print("Student subjects:")
for student, subjects in groups.items():
    print(f"  {student}: {subjects}")

# Grouping items
print("\nGrouping items:")

students = [
    {'name': 'Alice', 'grade': 'A'},
    {'name': 'Bob', 'grade': 'B'},
    {'name': 'Charlie', 'grade': 'A'},
    {'name': 'David', 'grade': 'C'},
    {'name': 'Eve', 'grade': 'B'}
]

by_grade = defaultdict(list)
for student in students:
    by_grade[student['grade']].append(student['name'])

print("By grade:")
for grade in sorted(by_grade.keys()):
    print(f"  Grade {grade}: {', '.join(by_grade[grade])}")

# Nested defaultdict
print("\nNested defaultdict:")

# Tree structure
tree = lambda: defaultdict(tree)
users = tree()

users['Alice']['age'] = 30
users['Alice']['city'] = 'NYC'
users['Bob']['age'] = 25
users['Bob']['city'] = 'LA'

print(f"Users: {dict(users)}")
print(f"Alice age: {users['Alice']['age']}")

# Graph representation
print("\nGraph representation:")

graph = defaultdict(list)

edges = [
    ('A', 'B'),
    ('A', 'C'),
    ('B', 'D'),
    ('C', 'D'),
    ('D', 'E')
]

for src, dst in edges:
    graph[src].append(dst)

print("Graph edges:")
for node, neighbors in sorted(graph.items()):
    print(f"  {node} -> {neighbors}")

# Word index
print("\nWord index:")

text = "the quick brown fox jumps over the lazy dog"
word_positions = defaultdict(list)

for i, word in enumerate(text.split()):
    word_positions[word].append(i)

print(f"Text: '{text}'")
print("Word positions:")
for word in sorted(word_positions.keys()):
    print(f"  '{word}': {word_positions[word]}")

# Custom default factory
print("\nCustom default factory:")

def default_value():
    return "N/A"

info = defaultdict(default_value)
info['name'] = 'Alice'
info['age'] = 30

print(f"Name: {info['name']}")
print(f"Age: {info['age']}")
print(f"City: {info['city']}")  # Returns "N/A"

# Matrix representation
print("\nMatrix representation:")

matrix = defaultdict(lambda: defaultdict(int))

matrix[0][0] = 1
matrix[0][2] = 3
matrix[1][1] = 5
matrix[2][0] = 7

print("Sparse matrix:")
for i in range(3):
    row = [matrix[i][j] for j in range(3)]
    print(f"  {row}")

# Convert to regular dict
print("\nConvert to regular dict:")

dd = defaultdict(list)
dd['a'].append(1)
dd['b'].append(2)

print(f"DefaultDict: {dd}")
print(f"Regular dict: {dict(dd)}")

# Access after conversion
regular = dict(dd)
try:
    regular['c'].append(3)
except KeyError:
    print("KeyError on regular dict (expected)")

# Frequency table
print("\nFrequency table:")

data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
freq = defaultdict(int)

for n in data:
    freq[n] += 1

print(f"Data: {data}")
print("Frequency:")
for num in sorted(freq.keys()):
    bar = 'â–ˆ' * freq[num]
    print(f"  {num}: {bar} ({freq[num]})")

deque.py
# deque examples

from collections import deque

# Basic deque
print("Basic deque:")

dq = deque([1, 2, 3, 4, 5])
print(f"Deque: {dq}")

# Append to right
dq.append(6)
print(f"After append(6): {dq}")

# Append to left
dq.appendleft(0)
print(f"After appendleft(0): {dq}")

# Pop from right
right = dq.pop()
print(f"Popped from right: {right}")
print(f"After pop(): {dq}")

# Pop from left
left = dq.popleft()
print(f"Popped from left: {left}")
print(f"After popleft(): {dq}")

# Extend deque
print("\nExtend deque:")

dq = deque([1, 2, 3])
print(f"Original: {dq}")

dq.extend([4, 5, 6])
print(f"After extend([4,5,6]): {dq}")

dq.extendleft([0, -1, -2])
print(f"After extendleft([0,-1,-2]): {dq}")

# Rotate deque
print("\nRotate deque:")

dq = deque([1, 2, 3, 4, 5])
print(f"Original: {dq}")

dq.rotate(2)
print(f"Rotate 2: {dq}")

dq.rotate(-3)
print(f"Rotate -3: {dq}")

# Maxlen deque
print("\nMaxlen deque:")

# Fixed-size deque
dq = deque(maxlen=3)

for i in range(1, 8):
    dq.append(i)
    print(f"Append {i}: {dq}")

# Recent items buffer
print("\nRecent items buffer:")

recent = deque(maxlen=5)

actions = ['open', 'save', 'edit', 'copy', 'paste', 'delete', 'undo', 'redo']

for action in actions:
    recent.append(action)
    print(f"Action '{action}': {list(recent)}")

# Queue operations
print("\nQueue operations:")

queue = deque()

# Enqueue
for item in ['A', 'B', 'C', 'D']:
    queue.append(item)
    print(f"Enqueue {item}: {queue}")

# Dequeue
while queue:
    item = queue.popleft()
    print(f"Dequeue {item}: {queue}")

# Stack operations
print("\nStack operations:")

stack = deque()

# Push
for item in [1, 2, 3, 4]:
    stack.append(item)
    print(f"Push {item}: {stack}")

# Pop
while stack:
    item = stack.pop()
    print(f"Pop {item}: {stack}")

# Palindrome check
print("\nPalindrome check:")

def is_palindrome(s):
    dq = deque(s.lower())
    while len(dq) > 1:
        if dq.popleft() != dq.pop():
            return False
    return True

words = ['racecar', 'hello', 'madam', 'python']
for word in words:
    print(f"'{word}': {is_palindrome(word)}")

# Circular buffer
print("\nCircular buffer:")

buffer = deque(maxlen=5)

for i in range(10):
    buffer.append(i)
    if i >= 4:
        print(f"Buffer: {list(buffer)} (avg: {sum(buffer)/len(buffer):.1f})")

# Sliding window
print("\nSliding window:")

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
window_size = 3

window = deque(maxlen=window_size)

for num in data:
    window.append(num)
    if len(window) == window_size:
        avg = sum(window) / len(window)
        print(f"Window {list(window)}: avg = {avg:.1f}")

# BFS queue
print("\nBFS queue:")

graph = {
    'A': ['B', 'C'],
    'B': ['D', 'E'],
    'C': ['F'],
    'D': [],
    'E': ['F'],
    'F': []
}

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    order = []
    
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            order.append(node)
            queue.extend(n for n in graph[node] if n not in visited)
    
    return order

print(f"BFS from 'A': {bfs(graph, 'A')}")

# Reverse with deque
print("\nReverse with deque:")

items = [1, 2, 3, 4, 5]
dq = deque(items)

dq.reverse()
print(f"Original: {items}")
print(f"Reversed: {list(dq)}")

# Index and count
print("\nIndex and count:")

dq = deque([1, 2, 3, 2, 4, 2, 5])
print(f"Deque: {dq}")
print(f"Count of 2: {dq.count(2)}")
print(f"Index of 2: {dq.index(2)}")
print(f"Index of 2 (start at 2): {dq.index(2, 2)}")

ordereddict.py
# OrderedDict examples

from collections import OrderedDict

# Basic OrderedDict
print("Basic OrderedDict:")

# Note: Regular dict preserves order in Python 3.7+
# OrderedDict has additional methods

od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3

print(f"OrderedDict: {od}")
print(f"Keys: {list(od.keys())}")

# Move to end
print("\nMove to end:")

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(f"Original: {od}")

od.move_to_end('a')
print(f"Move 'a' to end: {od}")

od.move_to_end('c', last=False)
print(f"Move 'c' to front: {od}")

# Pop from front
print("\nPop from front:")

od = OrderedDict([('first', 1), ('second', 2), ('third', 3)])
print(f"Original: {od}")

item = od.popitem(last=False)
print(f"Popped from front: {item}")
print(f"Remaining: {od}")

# Pop from end
print("\nPop from end:")

od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
print(f"Original: {od}")

item = od.popitem(last=True)
print(f"Popped from end: {item}")
print(f"Remaining: {od}")

# LRU cache simulation
print("\nLRU cache simulation:")

class LRUCache:
    def __init__(self, capacity):
        self.cache = OrderedDict()
        self.capacity = capacity
    
    def get(self, key):
        if key in self.cache:
            self.cache.move_to_end(key)
            return self.cache[key]
        return None
    
    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

cache = LRUCache(3)
cache.put('a', 1)
cache.put('b', 2)
cache.put('c', 3)
print(f"Cache: {cache.cache}")

cache.get('a')  # Access 'a'
print(f"After get('a'): {cache.cache}")

cache.put('d', 4)  # Evicts 'b'
print(f"After put('d', 4): {cache.cache}")

# Sorted dict
print("\nSorted dict:")

data = {'banana': 3, 'apple': 5, 'cherry': 2, 'date': 4}
sorted_dict = OrderedDict(sorted(data.items()))

print(f"Original: {data}")
print(f"Sorted: {sorted_dict}")

# Sort by value
sorted_by_value = OrderedDict(sorted(data.items(), key=lambda x: x[1]))
print(f"Sorted by value: {sorted_by_value}")

# Insertion order comparison
print("\nInsertion order comparison:")

# Two dicts with same items, different order
od1 = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
od2 = OrderedDict([('c', 3), ('b', 2), ('a', 1)])

print(f"OD1: {od1}")
print(f"OD2: {od2}")
print(f"Equal? {od1 == od2}")  # False (different order)

# Regular dicts ignore order
d1 = {'a': 1, 'b': 2, 'c': 3}
d2 = {'c': 3, 'b': 2, 'a': 1}
print(f"Regular dicts equal? {d1 == d2}")  # True

# Reverse iteration
print("\nReverse iteration:")

od = OrderedDict([('first', 1), ('second', 2), ('third', 3)])

print("Forward:")
for key, value in od.items():
    print(f"  {key}: {value}")

print("Reverse:")
for key, value in reversed(od.items()):
    print(f"  {key}: {value}")

# Maintain order in processing
print("\nMaintain order in processing:")

tasks = OrderedDict()
tasks['setup'] = 'Initialize system'
tasks['process'] = 'Process data'
tasks['validate'] = 'Validate results'
tasks['cleanup'] = 'Clean up resources'

print("Task sequence:")
for i, (name, description) in enumerate(tasks.items(), 1):
    print(f"  {i}. {name}: {description}")

# Recent items tracking
print("\nRecent items tracking:")

recent = OrderedDict()
max_recent = 5

actions = ['open', 'save', 'edit', 'copy', 'paste', 'open', 'delete', 'undo']

for action in actions:
    if action in recent:
        recent.move_to_end(action)
    else:
        recent[action] = True
        if len(recent) > max_recent:
            recent.popitem(last=False)
    
    print(f"After '{action}': {list(recent.keys())}")

# Dictionary with default order
print("\nDictionary with default order:")

# Python 3.7+ dicts are ordered, but OrderedDict has extra methods
config = OrderedDict([
    ('host', 'localhost'),
    ('port', 8080),
    ('debug', True),
    ('timeout', 30)
])

print("Config:")
for key, value in config.items():
    print(f"  {key} = {value}")

# Reorder
config.move_to_end('debug', last=False)
print("\nAfter moving debug to front:")
for key, value in config.items():
    print(f"  {key} = {value}")

chainmap.py
# ChainMap examples

from collections import ChainMap

# Basic ChainMap
print("Basic ChainMap:")

dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict3 = {'c': 5, 'd': 6}

cm = ChainMap(dict1, dict2, dict3)

print(f"Dict1: {dict1}")
print(f"Dict2: {dict2}")
print(f"Dict3: {dict3}")
print(f"ChainMap: {dict(cm)}")

# First occurrence wins
print(f"cm['a']: {cm['a']}")  # From dict1
print(f"cm['b']: {cm['b']}")  # From dict1
print(f"cm['c']: {cm['c']}")  # From dict2
print(f"cm['d']: {cm['d']}")  # From dict3

# Configuration hierarchy
print("\nConfiguration hierarchy:")

# Default config
defaults = {
    'host': 'localhost',
    'port': 8080,
    'debug': False,
    'timeout': 30
}

# User config
user_config = {
    'port': 9000,
    'debug': True
}

# Command line args
cli_args = {
    'host': '192.168.1.1'
}

# Priority: CLI > User > Defaults
config = ChainMap(cli_args, user_config, defaults)

print("Final config:")
for key in defaults.keys():
    print(f"  {key}: {config[key]}")

# Modifications
print("\nModifications:")

dict1 = {'a': 1}
dict2 = {'b': 2}

cm = ChainMap(dict1, dict2)
print(f"Original: {dict(cm)}")

# Update (modifies first dict)
cm['a'] = 10
cm['c'] = 3

print(f"After update: {dict(cm)}")
print(f"Dict1: {dict1}")  # Modified
print(f"Dict2: {dict2}")  # Unchanged

# New child
print("\nNew child:")

base = {'a': 1, 'b': 2}
cm = ChainMap(base)

print(f"Base: {dict(cm)}")

# Create new child context
cm = cm.new_child({'a': 10, 'c': 3})
print(f"With child: {dict(cm)}")

# Can go back
cm = cm.parents
print(f"Back to base: {dict(cm)}")

# Scope simulation
print("\nScope simulation:")

# Global scope
global_scope = {'x': 10, 'y': 20}

# Function scope
def function():
    local_scope = {'x': 5, 'z': 30}
    scope = ChainMap(local_scope, global_scope)
    
    print(f"  x (local): {scope['x']}")
    print(f"  y (global): {scope['y']}")
    print(f"  z (local): {scope['z']}")
    
    return scope

print("Inside function:")
function()

# Context managers
print("\nContext managers:")

settings = {'theme': 'light', 'font': 'Arial'}

print(f"Original: {settings}")

# Temporary override
temp = ChainMap({'theme': 'dark'}, settings)
print(f"Temporary: {dict(temp)}")

# Original unchanged
print(f"Original: {settings}")

# Environment variables
print("\nEnvironment variables:")

# System defaults
system = {'EDITOR': 'vi', 'SHELL': '/bin/sh'}

# User preferences
user = {'EDITOR': 'nano', 'PAGER': 'less'}

# Current session
session = {'EDITOR': 'code'}

env = ChainMap(session, user, system)

print("Environment:")
for key in ['EDITOR', 'SHELL', 'PAGER']:
    if key in env:
        print(f"  {key}: {env[key]}")

# Maps attribute
print("\nMaps attribute:")

dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}

cm = ChainMap(dict1, dict2, dict3)

print(f"Maps: {cm.maps}")
print(f"First map: {cm.maps[0]}")
print(f"All maps: {cm.maps}")

# Modify maps
cm.maps[0]['d'] = 4
print(f"After modification: {dict(cm)}")

# Parents
print("\nParents:")

dict1 = {'a': 1}
dict2 = {'b': 2}
dict3 = {'c': 3}

cm = ChainMap(dict1, dict2, dict3)

print(f"Full chain: {dict(cm)}")
print(f"Parents (skip first): {dict(cm.parents)}")
print(f"Parents.parents: {dict(cm.parents.parents)}")

# Fallback values
print("\nFallback values:")

primary = {'name': 'Alice', 'age': 30}
fallback = {'name': 'Unknown', 'age': 0, 'city': 'Unknown'}

data = ChainMap(primary, fallback)

print(f"Name: {data['name']}")
print(f"Age: {data['age']}")
print(f"City: {data['city']}")  # From fallback

# Merge vs ChainMap
print("\nMerge vs ChainMap:")

d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}

# Merge (creates new dict)
merged = {**d1, **d2}
print(f"Merged: {merged}")

# ChainMap (no copy)
chained = ChainMap(d1, d2)
print(f"Chained: {dict(chained)}")

# Modify original
d1['a'] = 10

print(f"After modifying d1:")
print(f"Merged: {merged}")  # Unchanged
print(f"Chained: {dict(chained)}")  # Reflects change

specialized containers Data structures optimized for specific use cases like counting, ordering, or double-ended access, extending built-in types.
Counter A dict subclass for counting hashable objects - automatically tallies elements and provides methods like most_common() for frequency analysis.
defaultdict A dict that calls a factory function for missing keys - eliminates KeyError checks and simplifies grouping patterns.
deque A list-like container with O(1) appends and pops from both ends - ideal for queues, sliding windows, and recent-item caches.
ChainMap Groups multiple dicts into a single view for lookups - useful for layered configurations like defaults, user settings, and command-line overrides.

Exercise: practical.py

Analyze word frequencies in text and group items by category