Learnixo

Python Essentials for AI Engineers · Lesson 17 of 36

Dictionary Methods: get, items, keys, values

Safe Access with get

Python
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")          # None

setdefault: Initialize if Missing

Python
# 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 pattern

update: Merging Dictionaries

Python
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

Python
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 inserted

Iterating: keys, values, items

Python
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

Python
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"] = 30

copy and Deep Copy

Python
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 affected

AI Patterns: Cache, Config, Metadata

LRU Cache with Dict

Python
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 oldest

Metadata Filtering for RAG

Python
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

Python
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 default

Dictionary 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) |