Python Essentials for AI Engineers · Lesson 17 of 36
Dictionary Methods: get, items, keys, values
Safe Access with get
config = {"model": "gpt-4o", "temperature": 0.0, "max_tokens": 2048}
# get(key, default): returns default if key is missing — no exception
model = config.get("model") # "gpt-4o"
timeout = config.get("timeout", 30) # 30 — key doesn't exist, returns default
missing = config.get("nonexistent") # None — no default specified
# Direct access raises KeyError if missing
# config["nonexistent"] # KeyError!
# Pattern: safe nested access
def get_nested(d: dict, *keys, default=None):
"""Access nested dict keys safely."""
for key in keys:
if not isinstance(d, dict):
return default
d = d.get(key, default)
return d
response = {"usage": {"prompt_tokens": 120, "completion_tokens": 80}}
tokens = get_nested(response, "usage", "prompt_tokens") # 120
cost = get_nested(response, "usage", "cost_usd") # Nonesetdefault: Initialize if Missing
# setdefault(key, default): returns value if key exists, else sets and returns default
cache = {}
def get_or_compute(cache: dict, key: str, compute_fn) -> str:
"""Simple memoization using setdefault."""
# If key exists: return existing value
# If key missing: call compute_fn(), store it, return it
return cache.setdefault(key, compute_fn())
# Grouping with setdefault — cleaner than if/else
drug_classes = [
("warfarin", "anticoagulant"),
("heparin", "anticoagulant"),
("metformin", "antidiabetic"),
("glipizide", "antidiabetic"),
]
grouped = {}
for drug, cls in drug_classes:
grouped.setdefault(cls, []).append(drug)
print(grouped)
# {"anticoagulant": ["warfarin", "heparin"], "antidiabetic": ["metformin", "glipizide"]}
# Note: defaultdict(list) is usually cleaner for this patternupdate: Merging Dictionaries
base_config = {
"model": "gpt-4o-mini",
"temperature": 0.7,
"max_tokens": 1024,
}
overrides = {
"temperature": 0.0, # Override
"max_tokens": 4096, # Override
"stream": True, # Add new key
}
# update: modifies in place
base_config.update(overrides)
print(base_config)
# {"model": "gpt-4o-mini", "temperature": 0.0, "max_tokens": 4096, "stream": True}
# Non-destructive merge: create a new dict
def merge_configs(*configs: dict) -> dict:
"""Later configs override earlier ones."""
result = {}
for cfg in configs:
result.update(cfg)
return result
final = merge_configs(base_config, {"model": "gpt-4o"})
print(final["model"]) # "gpt-4o" — overridden by last config
# Python 3.9+ merge operators
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 30}
merged = a | b # New dict — a unchanged
a |= b # In-place merge (same as update)pop and popitem
session_state = {
"user_id": "user_42",
"history": ["Q1", "Q2"],
"temp_context": "some big string",
"model": "gpt-4o",
}
# pop(key, default): remove and return — raises KeyError if no default provided
temp = session_state.pop("temp_context") # Removes and returns the value
print(temp) # "some big string"
print("temp_context" in session_state) # False — removed
missing = session_state.pop("nonexistent", None) # Safe — returns None
print(missing) # None
# popitem: remove and return an arbitrary (key, value) pair — LIFO in Python 3.7+
key, value = session_state.popitem()
print(key, value) # "model", "gpt-4o" — last insertedIterating: keys, values, items
scores = {"warfarin": 0.92, "aspirin": 0.78, "metformin": 0.61}
# keys(): iterate over keys — O(1) to get the view, O(n) to iterate
for drug in scores.keys():
print(drug)
# Equivalent: for drug in scores:
# values(): iterate over values
total = sum(scores.values())
print(f"Average score: {total / len(scores):.2f}")
# items(): iterate over (key, value) pairs — most common
for drug, score in scores.items():
if score >= 0.8:
print(f"{drug}: {score:.0%}") # "warfarin: 92%"
# All three return VIEWS — they reflect changes to the dict
# Don't add/remove keys while iterating (RuntimeError)
# Instead: iterate over a copy
for key in list(scores.keys()):
if scores[key] < 0.7:
del scores[key]
print(scores) # {"warfarin": 0.92, "aspirin": 0.78}Checking Membership
config = {"model": "gpt-4o", "temperature": 0.0}
# 'in' checks KEYS — O(1)
print("model" in config) # True
print("max_tokens" in config) # False
# Check value membership (O(n) — scans values)
print(0.0 in config.values()) # True
# Get with existence check
if "timeout" not in config:
config["timeout"] = 30copy and Deep Copy
import copy
original = {
"model": "gpt-4o",
"retrieval": {"k": 5, "threshold": 0.7},
}
# Shallow copy: new dict, same nested object references
shallow = original.copy()
shallow["model"] = "gpt-4o-mini" # Doesn't affect original
shallow["retrieval"]["k"] = 10 # DOES affect original (shared reference)
print(original["retrieval"]["k"]) # 10 — mutated!
# Deep copy: fully independent
original2 = {"model": "gpt-4o", "retrieval": {"k": 5, "threshold": 0.7}}
deep = copy.deepcopy(original2)
deep["retrieval"]["k"] = 10
print(original2["retrieval"]["k"]) # 5 — not affectedAI Patterns: Cache, Config, Metadata
LRU Cache with Dict
from collections import OrderedDict
class SimpleCache:
"""Exact-match cache for LLM responses."""
def __init__(self, max_size: int = 100):
self._cache: OrderedDict[str, str] = OrderedDict()
self._max_size = max_size
def get(self, key: str) -> str | None:
if key not in self._cache:
return None
self._cache.move_to_end(key) # Mark as recently used
return self._cache[key]
def set(self, key: str, value: str) -> None:
if key in self._cache:
self._cache.move_to_end(key)
self._cache[key] = value
if len(self._cache) > self._max_size:
self._cache.popitem(last=False) # Remove oldestMetadata Filtering for RAG
def build_metadata_filter(
drug_class: str | None = None,
min_year: int | None = None,
study_type: str | None = None,
) -> dict:
"""Build a Chroma/Pinecone metadata filter dict from optional criteria."""
filters: dict = {}
if drug_class is not None:
filters["drug_class"] = drug_class
if min_year is not None:
filters["year"] = {"$gte": min_year}
if study_type is not None:
filters["study_type"] = study_type
return filters
anticoag_filter = build_metadata_filter(drug_class="anticoagulant", min_year=2020)
print(anticoag_filter)
# {"drug_class": "anticoagulant", "year": {"$gte": 2020}}
# Pass to retriever
results = retriever.get_relevant_documents(
"warfarin dosing",
filter=anticoag_filter,
)Config Layering
DEFAULT_CONFIG = {
"model": "gpt-4o-mini",
"temperature": 0.0,
"max_tokens": 1024,
"timeout": 30,
"retry_attempts": 3,
}
CLINICAL_OVERRIDES = {
"temperature": 0.0,
"max_tokens": 4096,
}
USER_OVERRIDES = {
"model": "gpt-4o",
}
def build_config(*override_layers: dict) -> dict:
"""Apply override layers on top of defaults."""
config = DEFAULT_CONFIG.copy()
for layer in override_layers:
config.update(layer)
return config
final_config = build_config(CLINICAL_OVERRIDES, USER_OVERRIDES)
print(final_config["model"]) # "gpt-4o"
print(final_config["max_tokens"]) # 4096
print(final_config["timeout"]) # 30 — from defaultDictionary Methods Quick Reference
| Method | Description | Returns | Complexity |
|---|---|---|---|
| d[key] | Access value | value | O(1) avg |
| d.get(key, default) | Safe access | value or default | O(1) avg |
| d.setdefault(key, default) | Get or set-and-return | value | O(1) avg |
| d.update(other) | Merge other into d | None | O(k) |
| d.pop(key, default) | Remove and return | value | O(1) avg |
| d.popitem() | Remove and return last pair | (key, value) | O(1) |
| d.keys() | View of keys | KeysView | O(1) |
| d.values() | View of values | ValuesView | O(1) |
| d.items() | View of (k, v) pairs | ItemsView | O(1) |
| d.copy() | Shallow copy | dict | O(n) |
| d.clear() | Remove all items | None | O(n) |
| key in d | Membership check | bool | O(1) avg |
| d \| other (3.9+) | Merge, new dict | dict | O(n+k) |
| d \|= other (3.9+) | In-place merge | None | O(k) |