AI Skill Report Card
Using Collections Module
Quick Start15 / 15
Pythonfrom collections import namedtuple, deque, ChainMap, Counter, defaultdict, OrderedDict # Named fields instead of index access Point = namedtuple('Point', ['x', 'y']) p = Point(1, 2) # p.x == 1 # Fast appends/pops from both ends (O(1) vs list's O(n)) dq = deque([1, 2, 3]) dq.appendleft(0) dq.pop() # Frequency counting c = Counter("abracadabra") c.most_common(2) # [('a', 5), ('b', 2)] # Auto-initializing dict d = defaultdict(list) d['key'].append(1) # no KeyError, no manual init
Recommendation▾
Add a 'bad vs good' code comparison example to more strongly demonstrate anti-patterns from Common Pitfalls
Workflow14 / 15
-
Identify the access pattern driving container choice:
- Counting/tallying →
Counter - Queue/stack/sliding window →
deque - Tuple with named fields, immutable record →
namedtuple - Layered/fallback lookups across multiple dicts →
ChainMap - Need dict but with default values on missing key →
defaultdict - Need insertion-order guarantees pre-3.7 or
move_to_end→OrderedDict(note: plaindictis ordered since 3.7; useOrderedDictmainly for equality-by-order semantics ormove_to_end) - Need to subclass dict/list/str behavior →
UserDict/UserList/UserString
- Counting/tallying →
-
Pick the right constructor/factory:
namedtuple(typename, field_names)— supports_asdict(),_replace(),_fields, defaults viadefaults=paramdeque(iterable, maxlen=N)—maxlengives a fixed-size rolling bufferCounter(iterable_or_mapping)— supports+,-,&,|set-like operationsdefaultdict(default_factory)— factory called with no args (e.g.list,int,set, or alambda)ChainMap(*maps)— writes go to first map only; usenew_child()for scoped updates
-
Use built-in methods over manual logic:
Counter.most_common(n)instead of sorting manuallydeque.rotate(n)instead of slicing tricksnamedtuple._replace(**kwargs)instead of rebuilding tuplesChainMap.mapsto inspect/reorder the underlying list of mappings
-
Verify performance assumptions — deque is O(1) at both ends but O(n) for random access (
dq[i]is slow); uselistif you need indexing-heavy access.
Recommendation▾
Include a brief example of UserDict/UserList subclassing since it's mentioned but never shown in code
Examples17 / 20
Example 1: Input: Track word frequency in a large text and get the top 5 words. Output:
Pythonfrom collections import Counter words = text.lower().split() Counter(words).most_common(5)
Example 2: Input: Implement a sliding window of the last N items seen in a stream. Output:
Pythonfrom collections import deque window = deque(maxlen=N) for item in stream: window.append(item) # oldest auto-evicted
Example 3: Input: Build a config system where user settings override defaults override built-ins. Output:
Pythonfrom collections import ChainMap config = ChainMap(user_settings, defaults, builtins) config['theme'] # checks user_settings first, falls through
Example 4: Input: Group a list of (key, value) pairs into a dict of lists without checking for key existence. Output:
Pythonfrom collections import defaultdict grouped = defaultdict(list) for k, v in pairs: grouped[k].append(v)
Recommendation▾
Consider a decision table mapping problem symptoms to specific collections type for faster scanning
Best Practices
- Prefer
Counterarithmetic (c1 - c2,c1 & c2) over manual dict merging loops. - Use
namedtuple(..., defaults=(...))to avoid repetitive constructor calls when some fields are usually constant. - For
defaultdict, pick the factory to match desired zero-value:int→ 0,list→ [],set→ set(). - Convert
OrderedDictcomparisons only when order matters for equality — plain dicts compare by content regardless of order. - Use
ChainMap.new_child(m)to push a temporary scope (e.g., for nested function contexts) rather than copying/merging dicts. - When subclassing built-in types with extra invariants, prefer
UserDict/UserList/UserStringover subclassingdict/list/strdirectly — they avoid subtle bugs where built-in methods bypass overridden methods.
Common Pitfalls
- Don't index into a
dequein a hot loop — it's O(n); convert tolistfirst if random access is needed repeatedly. - Don't forget
Counter[missing_key]returns 0 instead of raisingKeyError— this can mask bugs when you actually wanted to detect missing keys. - Don't mutate a
defaultdict's structure through membership checks (if key in d) after accidentally accessingd[key]— the access itself inserts the default value. - Don't assume
ChainMapwrites propagate to all layers —chainmap[key] = valueonly ever modifieschainmap.maps[0]. - Don't use mutable default factories carelessly, e.g.
defaultdict(lambda: [])is fine, but sharing a single mutable object across keys (defaultdict(lambda: shared_list)) causes cross-key mutation bugs. - Don't rely on
OrderedDict-specific behavior when a plaindictsuffices — added complexity without benefit in Python 3.7+.