Skip to contentSkip to navigation
Module 04 / Dictionaries & SetsLesson 1 of 6Intermediate15 min read

Set Algebra & Venn Operations

Master hash-table collections: instantaneous $O(1)$ membership lookups, the Hashable protocol, mathematical set algebra (|, &, -, ^), and safe dictionary retrieval with .get().

01

Foundations & Core Architecture

3-Tier Learning Ladder
🟢 Level 1: Set Algebra & RBAC→🟡 Level 2: Tag Pipelines & Safe .get()→🔴 Level 3: Crawler Deduplication Gateway

Why Master Sets & Dictionaries?

In software engineering, performance frequently hinges on choosing the right data structure. If you search for an item in an unsorted list of 1,000,000 elements, Python must scan every item in O(N) linear time. In contrast, querying a set or dictionary takes instantaneous O(1) constant time, regardless of whether the collection holds 10 or 10,000,000 items. Sets and dictionaries form the operational backbone of caching layers (Redis-style key-value stores), database index lookups, permission access-control lists (RBAC), and web deduplication engines. Understanding how hash tables work under the hood is essential for writing performant, scalable Python systems.

1. O(1) Hash Table Foundations

Unlike sequential integer-indexed lists, sets and dictionaries convert elements into 64-bit integer hashes via CPython's hash() function, allowing direct memory bucket access in amortized $O(1)$ constant time.

2. The Hashable Protocol

An object is hashable only if its hash never mutates during its lifetime. Immutable types (int, str, tuple, frozenset) are hashable; mutable containers (list, dict, set) are unhashable.

3. Mathematical Set Algebra

Python sets natively execute Venn diagram operations: union (|), intersection (&), difference (-), and symmetric difference (^) for high-speed relational queries.

4. Safe Retrieval & Fallbacks

Direct bracket indexing d[key] halts execution with a KeyError on missing keys. In contrast, d.get(key, fallback) gracefully provides a default value, essential for resilient APIs.

Curriculum Learning Objectives

  • Contrast hash-based collections (sets, dictionaries) with sequence collections (lists, tuples) in terms of indexing, order, and O(1) lookup performance.
  • Construct and manipulate Python sets, applying automatic deduplication and set algebra operators (|, &, -, ^).
  • Build and query dictionaries using key-value associations, distinguishing between direct bracket indexing and safe .get() fallback retrieval.
  • Analyze the Hashable protocol, identifying why mutable objects (lists, dicts, sets) cannot serve as dictionary keys or set members.
  • Avoid critical initialization bugs, specifically recognizing that {} creates an empty dict while set() creates an empty set.
  • Implement high-performance data processing patterns: membership testing, deduplication pipelines, and multi-set relational filtering.
02

Syntax Anatomy & The Empty Collection Trap

Python Syntax Trap: Empty Dictionary vs. Empty Set

Because dictionaries existed in Python over a decade before sets were introduced in Python 2.3, the empty curly braces {} were already reserved for dictionaries!

d = {}

Creates an empty dict! Calling d.add(1) triggers AttributeError: 'dict' object has no attribute 'add'.

s = set()

Creates an empty set! Calling s.add(1) succeeds and stores {1}.

Set Operators: | & - ^Mathematical Algebra

Direct relational operators between two Python sets:

a | b // Union (elements in either)
a & b // Intersection (elements in both)
a - b // Difference (in a but not in b)
a ^ b // Symmetric Diff (in a or b, not both)
d.get(key, fallback)Crash-Proof

Safe value extraction with optional default fallback:

user = {"role": "admin"}
user.get("email", "none") → 'none'
user["email"] → Raises KeyError!
s.discard(x) vs. s.remove(x)Idempotency

Deleting items from a set:

s.discard("missing") → Safe! Does nothing.
s.remove("missing") → Raises KeyError!
frozenset([items])Immutable Set

An immutable, hashable set suitable as a dictionary key:

fs = frozenset(["read", "write"])
cache = {fs: "token_abc"} // Valid key!
03

Execution Architecture: CPython Hash Table & Hashability

How CPython Resolves Lookups in Amortized O(1) Time

When you evaluate "admin" in user_roles or cache["key"], CPython calculates the 64-bit integer hash code using hash("admin"), masks it against the bucket array size, and immediately jumps to that memory slot. If two distinct keys hash to the same bucket index (a collision), CPython uses open addressing with a perturb shift algorithm to probe subsequent candidate slots until the exact key is matched.

Visual Architecture: Hash Table Lookup Mechanics

Key: "admin" ---> hash("admin") = 8392104928103948123
                        |
                 [ Mask to Table Size: & (N - 1) ]
                        |
                        v
                 Bucket Index: #4
                        |
                 +--------------+
      Index #4   | Key: "admin" | ---> Points to Value: {"role": "root"}
                 +--------------+
                 (Direct O(1) Memory Lookup! Zero linear scanning!)

The Golden Rule of Hashability: Why Mutable Lists Crash As Keys

For an object to live in a set or serve as a dictionary key, it must implement __hash__() and __eq__(). The hash must remain constant for the object's entire lifetime. If Python permitted mutable lists as keys:

k = [1, 2]
d[k] = "data" // Placed in bucket #3 based on hash([1, 2])
k.append(3) // MUTATION! Hash changes, but item remains in bucket #3!
d[k] // Searches bucket #7 -> KEY LOST FOREVER IN MEMORY!

To prevent table corruption, Python fails-closed by disabling __hash__() on all mutable containers (list, dict, set).

04

Collection Comparison Matrix

Data StructureSyntaxLookup TimeMutable?Duplicates?Ordering GuaranteeHashable?
List (list)[1, 2, 3]O(N) searchYesAllowedInsertion Index (0..N-1)No
Tuple (tuple)(1, 2, 3)O(N) searchNoAllowedInsertion Index (0..N-1)Yes (if contents are)
Set (set){1, 2, 3}O(1) averageYesDeduplicatedUnorderedNo
Dict (dict){"k": "v"}O(1) averageYesUnique KeysInsertion Order (3.7+)No
FrozenSet (frozenset)frozenset([1, 2])O(1) averageNoDeduplicatedUnorderedYes
05

Interactive Syntax Reference & Sandbox

Initializing Sets vs. Dictionaries

Demonstrates the empty constructor syntax and literal forms.

Empty collection initialization
# 1. Empty constructors
empty_dict = {}
empty_set = set()

print("empty_dict type:", type(empty_dict).__name__) # 'dict'
print("empty_set type: ", type(empty_set).__name__)  # 'set'

# 2. Populated literals
s = {1, 2, 2, 3, 3, 3}
print("Populated set (deduplicated):", sorted(s))

d = {"name": "Alice", "role": "admin"}
print("Populated dict:", d)
06

Tiered Code Walkthroughs (Worked Examples)

Level 1 · Foundation

Role-Based Access Control (RBAC) Permission Verifier

Engineering Requirement

Implement an API gateway security gatekeeper. Each incoming request requires a specific set of security scopes (e.g. {'read', 'write'}). The verifier must inspect the authenticated user's permission set, determine whether all required scopes are met, and report any missing or excess permissions.

Solution Implementation: Role-Based Access Control (RBAC) Permission Verifier
def verify_rbac_access(user_perms: set[str], required_perms: set[str]) -> dict:
    """
    Verifies user permissions using O(1) set algebra operations.
    """
    # 1. Check if user possesses all required permissions
    is_authorized = required_perms.issubset(user_perms)
    
    # 2. Compute missing permissions using set difference
    missing = required_perms - user_perms
    
    # 3. Compute granted permissions using set intersection
    granted = user_perms & required_perms
    
    # 4. Compute excess permissions using set difference
    excess = user_perms - required_perms
    
    return {
        "authorized": is_authorized,
        "missing": sorted(missing),
        "granted": sorted(granted),
        "excess": sorted(excess)
    }

# Test scenarios:
admin_tokens = {"read", "write", "delete", "audit"}
editor_tokens = {"read", "write"}
viewer_tokens = {"read"}

endpoint_requirement = {"read", "write", "delete"}

print("=" * 60)
print(f"{'RBAC GATEWAY ACCESS EVALUATOR':^60}")
print("=" * 60)

for role_name, token_set in [("Admin", admin_tokens), ("Editor", editor_tokens), ("Viewer", viewer_tokens)]:
    result = verify_rbac_access(token_set, endpoint_requirement)
    status = "AUTHORIZED" if result["authorized"] else "FORBIDDEN"
    print(f"Role: {role_name:<8} -> Status: {status}")
    if not result["authorized"]:
        print(f"   Missing Scopes: {result['missing']}")
    else:
        print(f"   Excess Scopes:  {result['excess']}")
    print("-" * 60)
Architectural Breakdown & Performance Highlights

This beginner example demonstrates the power of set theory in security architectures. Checking `required_perms.issubset(user_perms)` determines authorization instantaneously. The set difference operator `required_perms - user_perms` precisely isolates missing scopes without messy loops or nested conditionals.

Level 2 · Applied

User Profile Sanitizer & Tag Deduplication Pipeline

Engineering Requirement

Create an ingestion sanitizer for an API that receives raw user profile payloads. Raw payloads contain irregular whitespace, inconsistent casing, duplicate interest tags, and missing optional metadata. The pipeline must clean string values, provide default fallbacks for missing keys using dict.get(), and deduplicate tags using a set.

Solution Implementation: User Profile Sanitizer & Tag Deduplication Pipeline
def sanitize_user_profile(raw_payload: dict) -> dict:
    """
    Sanitizes raw user dictionaries with safe key retrieval and tag deduplication.
    """
    # 1. Safe extraction with get() fallbacks
    user_id = raw_payload.get("user_id", "anonymous")
    
    raw_name = raw_payload.get("display_name", "Guest User")
    clean_name = raw_name.strip().title() if isinstance(raw_name, str) else "Guest User"
    
    tier = raw_payload.get("tier", "free").strip().lower()
    
    # 2. Extract and deduplicate interest tags using a set
    raw_tags = raw_payload.get("tags", [])
    # Case-normalize and strip tags, then deduplicate in a set
    clean_tag_set = {t.strip().lower() for t in raw_tags if isinstance(t, str) and t.strip()}
    
    # 3. Construct clean record
    return {
        "user_id": user_id,
        "display_name": clean_name,
        "tier": tier,
        "tags": sorted(clean_tag_set),  # Store sorted for deterministic output
        "verified": raw_payload.get("verified", False)
    }

incoming_json = {
    "user_id": "usr_8831",
    "display_name": "   dr. alexander v. cross   ",
    "tier": "ENTERPRISE",
    "tags": ["Machine Learning", "ai", "CLOUD", "ai", "Machine Learning ", "python"]
    # 'verified' key is intentionally omitted from payload
}

clean_record = sanitize_user_profile(incoming_json)
print("=" * 60)
print(f"{'INGESTED USER PROFILE NORMALIZATION':^60}")
print("=" * 60)
for k, v in clean_record.items():
    print(f"{k:<15}: {v}")
print("=" * 60)
Architectural Breakdown & Performance Highlights

This intermediate example models web backend data ingestion. Using `raw_payload.get('verified', False)` prevents `KeyError` crashes when optional fields are omitted. Tag normalization combines set comprehension `{t.strip().lower() for t in raw_tags}` to automatically discard duplicates like 'ai' and 'Machine Learning' in a single clean step.

Level 3 · Architecture

High-Throughput Web Crawler URL Deduplication Gateway

Engineering Requirement

Build an asynchronous web crawler URL frontier gateway. Given a batch of newly discovered URLs and a persistent cache of already visited URLs, the gateway must: 1) discard all previously visited URLs using set difference; 2) partition new URLs by host domain into a routing dictionary; 3) update the visited cache in-place with union assignment.

Solution Implementation: High-Throughput Web Crawler URL Deduplication Gateway
def process_crawler_batch(
    incoming_urls: list[str],
    visited_cache: set[str]
) -> tuple[dict[str, list[str]], set[str]]:
    """
    Filters unvisited URLs and partitions them by domain destination.
    """
    # 1. Convert incoming URLs to set (deduplicates within the batch)
    incoming_set = set(incoming_urls)
    
    # 2. Filter out already visited URLs using set difference: O(N)
    new_urls = incoming_set - visited_cache
    
    # 3. Partition by domain into a routing dictionary
    domain_routes = {}
    for url in sorted(new_urls):
        # Extract domain: 'https://docs.pylabs.org/page' -> 'docs.pylabs.org'
        if "://" in url:
            domain = url.split("://")[1].split("/")[0]
        else:
            domain = url.split("/")[0]
            
        domain_routes.setdefault(domain, []).append(url)
        
    # 4. In-place update of visited cache using union assignment |=
    visited_cache |= new_urls
    
    return domain_routes, visited_cache

# Initial state: URLs visited in previous crawl cycles
persistent_visited_cache = {
    "https://pylabs.org/",
    "https://docs.pylabs.org/intro",
    "https://blog.pylabs.org/v1-release"
}

# Inbound crawler stream containing duplicates and already-visited links
fresh_batch = [
    "https://pylabs.org/",              # Visited
    "https://pylabs.org/pricing",        # New
    "https://docs.pylabs.org/python",    # New
    "https://docs.pylabs.org/intro",     # Visited
    "https://api.pylabs.org/health",     # New
    "https://pylabs.org/pricing"         # Duplicate in current batch
]

print("=" * 65)
print(f"{'CRAWLER URL DEDUPLICATION & ROUTING GATEWAY':^65}")
print("=" * 65)
print(f"Initial Cache Size:  {len(persistent_visited_cache)} URLs")
print(f"Incoming Batch Size: {len(fresh_batch)} URLs")
print("-" * 65)

routes, updated_cache = process_crawler_batch(fresh_batch, persistent_visited_cache)

print("Dispatched Domain Queue Routes:")
for domain, queue in routes.items():
    print(f"  [{domain}] ({len(queue)} items):")
    for u in queue:
        print(f"     -> {u}")

print("-" * 65)
print(f"Updated Cache Size:  {len(updated_cache)} URLs")
print("=" * 65)
Architectural Breakdown & Performance Highlights

This advanced example demonstrates high-scale data pipeline engineering. By converting the incoming batch to a set and computing `incoming_set - visited_cache`, millions of redundant HTTP requests are eliminated in linear time. The routing dictionary organizes crawler workers per domain to respect rate-limiting policies, while `visited_cache |= new_urls` atomically records progress.

07

Anti-Patterns & Critical Pitfalls

Avoid these 6 common traps—from the empty set initialization gotcha to mutating hash tables during active iteration.

[Common Trap] The Empty Set Initialization Trap
Defective Implementation
s = {}  # BUG: Intended empty set, but created empty dict!
s.add(42)  # AttributeError: 'dict' object has no attribute 'add'
Failure Symptom: AttributeError: 'dict' object has no attribute 'add'
Production-Safe Solution
s = set()  # Always use set() for an empty set
s.add(42)
Root Cause: In Python, {} is the literal syntax for an empty dictionary, not an empty set.
[Common Trap] Using Unhashable Mutable Types as Dict Keys or Set Items
Defective Implementation
cache = {}
cache[[1, 2, 3]] = "result"  # BUG: list cannot be a dict key!
Failure Symptom: TypeError: unhashable type: 'list'
Production-Safe Solution
# Convert mutable lists to immutable tuples:
cache[(1, 2, 3)] = "result"
Root Cause: Lists are mutable and do not implement __hash__(). Modifying a list in place would corrupt the hash table bucket address.
[Subtle Trap] Direct Bracket Indexing on Missing Keys
Defective Implementation
user = {"name": "Alice"}
email = user["email"]  # BUG: Raises KeyError when 'email' is absent!
Failure Symptom: KeyError: 'email'
Production-Safe Solution
email = user.get("email", "default@pylabs.org")  # Use .get() with fallback
Root Cause: Bracket syntax d[k] raises a KeyError if k does not exist in the dictionary.
[Subtle Trap] Modifying a Set or Dict While Iterating Over It
Defective Implementation
nums = {1, 2, 3, 4, 5}
for n in nums:
    if n % 2 == 0:
        nums.remove(n)  # BUG: Mutating set during iteration!
Failure Symptom: RuntimeError: Set changed size during iteration
Production-Safe Solution
# Iterate over a static copy or use set difference:
nums -= {n for n in nums if n % 2 == 0}
Root Cause: Mutating a hash table during iteration invalidates internal bucket traversal pointers.
[Architecture Trap] Confusing set.remove() with set.discard()
Defective Implementation
active_users = {"alice", "bob"}
active_users.remove("carol")  # BUG: Raises KeyError if 'carol' not in set!
Failure Symptom: KeyError: 'carol'
Production-Safe Solution
active_users.discard("carol")  # Safe deletion without KeyError
Root Cause: remove() raises KeyError when the target is absent; discard() deletes it if present and does nothing otherwise.
[Architecture Trap] Assuming Set Elements Preserve Insertion Order
Defective Implementation
s = set()
s.add("first")
s.add("second")
s.add("third")
first_elem = list(s)[0]  # BUG: May not be 'first'!
Failure Symptom: Nondeterministic ordering across runs or platform restarts.
Production-Safe Solution
# If order is required, use a list, dict, or dict.fromkeys():
ordered_unique = list(dict.fromkeys(["first", "second", "third"]))
Root Cause: Sets are inherently unordered collections. CPython sets do not preserve insertion order.
08

Real-World Production Architectures

Developer ProductivityFoundation Script

Local Developer Automation Tool

Individual developers and IT administrators automate everyday repetitive workflows.

admins = {"alice", "bob"}
moderators = {"bob", "carol"}
both = admins & moderators  # Intersection
print("Admins who are also moderators:", both)
A clean standalone script providing fast, local automation with zero dependencies.
Cloud MicroservicesScaled Service · Applied

Backend API Service Integration

SaaS platforms run backend microservices processing requests concurrently.

# Service integration layer
def handle_request(payload: dict) -> dict:
    return {"status": "success", "processed": True}
A modular service handler integrating validation and structured responses.
Distributed SystemsEnterprise · Architecture

High-Throughput Cloud Architecture

Social networks like Twitter use set intersection to calculate mutual followers and friend suggestions in microseconds.

# High-resilience production pattern
import time

def process_stream(events: list) -> int:
    processed = 0
    for event in events:
        if event:
            processed += 1
    return processed
Social networks like Twitter use set intersection to calculate mutual followers and friend suggestions in microseconds.
09

Production Case Study: Distributed RBAC Security Gateway

Security Architecture: Zero-Loop Token Scope Authorization
Sub-Microsecond Evaluation

In microservices handling millions of incoming requests per second, evaluating whether an authenticated OAuth2 bearer token possesses the necessary permissions for an API endpoint cannot afford linear loop traversals. By caching user scopes as a frozenset, authorization becomes a single, highly optimized C-level subset query endpoint_scopes.issubset(token_scopes), executing in under 0.3 microseconds per request.

# Precompiled FrozenSet Requirements on Route Definition:
ROUTE_SCOPES = frozenset(["billing:read", "billing:export"])

# O(1) Instantaneous Invariant Check on Request Ingestion:
def authorize_request(token_scopes: frozenset[str]) -> bool:
    return ROUTE_SCOPES.issubset(token_scopes)
10

Guided Practice Labs

Lab 1

Mutual Friends Finder

Easy

Write a function `find_mutual_friends(user_a: list[str], user_b: list[str]) -> list[str]` that takes two lists of friend usernames and returns a sorted list containing only the mutual friends shared by both users.

Starter Template:
def find_mutual_friends(user_a: list[str], user_b: list[str]) -> list[str]:
    # TODO: Return sorted list of mutual friends
    pass

# Test:
# find_mutual_friends(["alice", "bob", "carol"], ["bob", "dave", "carol"]) -> ["bob", "carol"]
Lab 2

Multi-Store Inventory Merger

Medium

Write a function `merge_inventories(store_a: dict[str, int], store_b: dict[str, int]) -> dict[str, int]` that combines item stock counts from two store branches. If an item exists in both stores, its quantities must be summed. If an item exists in only one store, its quantity is preserved.

Starter Template:
def merge_inventories(store_a: dict[str, int], store_b: dict[str, int]) -> dict[str, int]:
    # TODO: Merge two inventories summing shared items
    pass

# Test:
# merge_inventories({"apples": 5, "oranges": 10}, {"apples": 3, "bananas": 8})
# -> {"apples": 8, "oranges": 10, "bananas": 8}
11

Independent Practice Tasks

Task 1Beginner

Venn Anomaly Detector

Write find_anomalies(baseline, sample) using symmetric difference baseline ^ sample to isolate newly introduced or dropped telemetry metrics.

Task 2Intermediate

Composite Permission Hash

Construct a security dispatcher that maps frozenset([role, scope]) tuples to authorized rate limits, proving that frozensets function as valid dictionary keys.

Task 3Advanced

Multi-Source Data Reconciler

Combine three database cluster exports (Node A, Node B, Node C) using set unions and intersections to identify consensus records present on all 3 nodes.

12

Key Takeaways & Cheat Sheet

Core Invariants

  • Sets and dictionaries rely on hash tables, providing instantaneous O(1) average-time lookups, insertions, and deletions.
  • Sets store unique elements only; duplicate additions are discarded automatically.
  • Empty curly braces {} create an empty dictionary; use set() to create an empty set.
  • Set elements and dictionary keys must be hashable; mutable objects (lists, sets, dicts) are unhashable.
  • Use d.get(key, fallback) for safe value retrieval to avoid fatal KeyError exceptions.
  • Use s.discard(item) instead of s.remove(item) when you do not want missing items to raise an error.
  • Set algebra operators (|, &, -, ^) enable declarative, high-speed relational comparisons.

Quick Reference Card

s = set() — Create an empty set (NOT {}).
d = {} — Create an empty dictionary.
s.add(x) / s.discard(x) — Add or safely remove item from set.
a | b / a & b — Set union / intersection.
a - b / a ^ b — Set difference / symmetric difference.
d.get(k, default) — Safe dictionary lookup with fallback.
d.items() — Iterate over (key, value) pairs.
d.keys() / d.values() — Iterate over keys / values.
frozenset(iterable) — Immutable, hashable set suitable as dict key.
13

Interactive Knowledge Booster (17 Questions)

Test your mastery of hash tables, the Hashable protocol, and set algebra operators. Instant feedback explains each question.

Q1[easy]
What does a set do with duplicate values?
Q2[easy]
Which operator finds values shared by two sets?
Q3[medium]
Which is safer when a dictionary key might be absent?
Q4[easy]
What does this print?
colors = {'red', 'red', 'blue'}
print(len(colors))
Q5[easy]
What does this print?
a = {1, 2, 3}
b = {3, 4}
print(sorted(a & b))
Q6[medium]
What does this print?
student = {'name': 'Ivy'}
print(student.get('level', 'beginner'))
Q7[medium]
Which line is the bug if you want to add 'green' to a set named colors?
Q8[medium]
Which line removes the key 'city' and its value from a dictionary named profile?
Q9[hard]
What does this print?
data = {'a': 1}
data['a'] = 2
data['b'] = 3
print(data['a'] + data['b'])
Q10[hard]
What does this print?
a = {1, 2, 3}
b = {2, 4}
print(sorted(a - b))
Q11[easy]
What data type does the expression x = {} create?
Q12[medium]
What happens when you execute: s = set(); s.add([1, 2, 3])?
Q13[medium]
What is the difference between s.remove(x) and s.discard(x)?
Q14[medium]
What does {1, 2, 3} ^ {2, 3, 4} produce?
Q15[hard]
Why can a tuple be used as a dictionary key while a list cannot?
Q16[hard]
What is the average time complexity of checking 'item in set' versus 'item in list' for N elements?
Q17[easy]
What does this print?
d = {'a': 10, 'b': 20}
print(d.get('c', 0) + d.get('a', 0))
14

Frequently Asked Questions & Study Guide

Q: Why does s = {} create a dictionary instead of a set?

Dictionaries were part of Python from its initial release in 1991, using {} as literal syntax. Sets were added much later in Python 2.3. To preserve backwards compatibility for existing code, {} remained dedicated to empty dictionaries, requiring set() for empty sets.

Q: Why can't I use a list as a dictionary key or set item?

Hash tables require keys to be immutable so their hash code (and bucket location) never changes. Because lists are mutable, their contents can change at runtime, which would misplace the item in the hash table. Use an immutable tuple instead: (1, 2, 3).

Q: What happens if two different keys have the same hash code?

This is called a hash collision. Python handles collisions gracefully using open addressing with a perturb shift algorithm. It probes subsequent candidate slots in the table until it finds either the matching key or an empty slot, ensuring correctness even when hashes collide.

Q: When should I use s.discard() instead of s.remove()?

Use s.discard(x) when you want idempotent removal (remove x if it exists, otherwise do nothing). Use s.remove(x) only when the absence of x indicates an exceptional error condition that should raise a KeyError.

Your notes

Sign in to keep private notes alongside this lesson.

Sign in to take notes

Set Algebra & Venn Operations practice

Sign-in required

Create fruit = {'apple': 3, 'pear': 2}. Add 'banana' with value 5, then print fruit['banana'].

Expected output: 5

Coding Practice is Locked

Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.

Practice quiz

Check yourself

15 questions with explanations after submission, so you can correct course as you go.

Set Algebra & Venn Operations quiz

Locked

You must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.

Lesson incomplete

Not completed

Create an ID or sign in to save lesson completion across devices.

Create ID to save
Module lesson progress0/6 complete
Module cheat sheet