Free Study Material

Dictionary In Python

Introduction

Lists and tuples locate a value by its numeric position. A dictionary locates a value by a name you choose yourself — a far more natural fit for data like a student's marks in different subjects.

Concept

A dictionary stores data as key-value pairs, written inside curly braces {}. Each key maps to exactly one value, and every key in a dictionary must be unique. A dictionary is mutable — pairs can be added, changed, or removed after creation.

student = {"name": "Aisha", "marks": 91}

Keys must be an immutable type — commonly a string or a number — since Python needs a key's value to never change in order to reliably locate it again. Values can be absolutely anything, including another list or dictionary.

Worked Example

marks = {"Physics": 85, "Chemistry": 78, "Maths": 92}

print(marks)
print(type(marks))
print(marks["Physics"])

marks maps each subject name (the key) to a score (the value). Printing the whole dictionary shows all three pairs together. type(marks) prints <class 'dict'>. marks["Physics"] reaches the value paired with the key "Physics", giving 85 — by key name, not by numeric position.

Note: {} on its own creates an empty dictionary, not an empty set — even though sets also use curly braces. This is a common point of confusion, since {} is genuinely ambiguous-looking, but Python always treats bare {} as a dictionary.

Explanation

A dictionary is the right structure specifically when data is naturally described by a label rather than a position — marks["Physics"] is more direct and more readable than needing to remember that Physics happens to be stored at index 0 of some separate list, which is exactly the kind of lookup a list or tuple would force you into instead.

Summary

A dictionary stores key-value pairs in curly braces {}, with every key unique and immutable, and values of any type. A value is reached by its key (my_dict["key"]), not by numeric position, and the dictionary itself is mutable after creation.

Definition

A Dictionary is an unordered collection of items where each item consists of a Key and a Value. It is enclosed in Curly Braces { }.

The "Key-Value" Logic:

  • Key (Label): Must be Unique and Immutable (Strings, Numbers, Tuples). You use this to find data.
  • Value (Data): Can be Anything (Mutable or Immutable, Duplicates allowed). This is the actual data.
Example: { "Brand": "Nike", "Price": 5000 }

Syntax

my_dict = { "Key1" : Value1, "Key2" : Value2 }
# 1. Empty Dictionary
empty = {}

# 2. Student Record (Strings as Keys)
student = {
    "Name": "Rahul",
    "Age": 16,
    "Stream": "CS"
}
print("Student Data:", student)

# 3. Number Mapping (Integers as Keys)
rankings = {
    1: "Gold",
    2: "Silver",
    3: "Bronze"
}
print("Rank 1 Prize:", rankings[1])

Rules for Keys

  • Unique: You cannot have two keys with the same name. If you do, the second one overwrites the first.
  • Immutable: You can use a String, Number, or Tuple as a key. You cannot use a List (because lists can change).
# Rule 1: Duplicate Keys overwrite previous ones
data = {
    "Score": 100,
    "Score": 95   # This overwrites 100!
}
print("Duplicate Test:", data) 
# Output: {'Score': 95}

# Rule 2: Mutable Keys are Forbidden
# Uncommenting the next line causes an Error
# invalid = { ["A", "B"] : "List Key" } 
# TypeError: unhashable type: 'list'

Introduction

This topic covers reading a value safely, adding a brand-new pair, and changing an existing one — the everyday operations a dictionary is used for.

Concept

  • my_dict[key] — returns the value for key. Raises KeyError if key doesn't exist.
  • my_dict.get(key) — returns the value for key, or None (instead of an error) if it doesn't exist.
  • my_dict[key] = value — adds a new pair if key doesn't already exist, or overwrites the existing value if it does — the same syntax handles both.

Worked Example

marks = {"Physics": 85, "Chemistry": 78}

print(marks["Physics"])
print(marks.get("Maths"))

marks["Maths"] = 92
print(marks)

marks["Physics"] = 90
print(marks)

marks["Physics"] gives 85. marks.get("Maths") returns None, since "Maths" isn't a key yet — no error is raised. marks["Maths"] = 92 adds a brand-new pair, since "Maths" wasn't present before: {'Physics': 85, 'Chemistry': 78, 'Maths': 92}. marks["Physics"] = 90 then overwrites the existing value for "Physics", since that key already exists: {'Physics': 90, 'Chemistry': 78, 'Maths': 92}.

Warning: marks["Maths"] (using [ ] instead of .get()) on a dictionary that doesn't yet have that key raises KeyError: 'Maths' and stops the program — use .get() whenever a key's absence is a normal possibility you need to check for, not an error.

Explanation

Whether my_dict[key] = value adds or overwrites depends entirely on one thing — whether key was already present — and that single rule is exactly what makes updating a dictionary and adding to one use the identical line of code, unlike a list, which needs separate operations (append() vs. index assignment) for the two cases.

Summary

my_dict[key] reads a value and raises KeyError if the key is missing; my_dict.get(key) reads a value and returns None instead if it's missing. my_dict[key] = value both adds a new pair and overwrites an existing one, depending only on whether that key was already present.

Syntax: Retrieval

Method 1: Square Brackets
value = dict["Key"]

⚠️ Crashes (KeyError) if key not found.

Method 2: .get() (Safe)
value = dict.get("Key")

✅ Returns 'None' if key not found.

user = {
    "username": "coder123",
    "level": 5
}

# 1. Successful Access
print("User:", user["username"])  # Output: coder123

# 2. The Danger of Brackets
# print(user["score"])  <-- ERROR! KeyError: 'score'

# 3. The Safety of .get()
print("Score:", user.get("score")) # Output: None
print("Level:", user.get("level")) # Output: 5
prices = {"Apple": 100, "Banana": 40}
print("Original:", prices)

# Case A: Modifying (Key 'Apple' exists)
prices["Apple"] = 120
print("After Update:", prices)
# Result: Apple is now 120

# Case B: Adding (Key 'Mango' does not exist)
prices["Mango"] = 80
print("After Add:", prices)
# Result: Mango is added to the end
# Deleting a key
del prices["Banana"]
print("After Delete:", prices)
# Result: Banana is gone. Warning: Crashes if key isn't there!

Introduction

You've already used get() to look up a value safely. This topic covers two more methods that go a step further — supplying a fallback value, and updating several pairs at once.

Concept

  • get(key, default) — like plain get(key), but returns default instead of None when key is missing, instead of just leaving you with nothing to work with.
  • update(other_dict) — merges another dictionary's pairs into this one: existing keys get their values overwritten, and new keys get added.
  • setdefault(key, default) — returns the value for key if it exists; if it doesn't, it adds key with default as its value, and returns that.

Worked Example

marks = {"Physics": 85, "Chemistry": 78}

print(marks.get("Maths", 0))
print(marks)

marks.update({"Maths": 92, "Physics": 88})
print(marks)

result = marks.setdefault("Biology", 0)
print(result)
print(marks)

marks.get("Maths", 0) returns 0 — the fallback — since "Maths" isn't a key; marks itself is left completely unchanged by get(). marks.update({"Maths": 92, "Physics": 88}) adds the new key "Maths" and overwrites the existing "Physics" value in a single call: {'Physics': 88, 'Chemistry': 78, 'Maths': 92}. marks.setdefault("Biology", 0) finds no "Biology" key, so it adds 'Biology': 0 to marks and returns 0 — unlike get(), setdefault() genuinely changes the dictionary when the key is missing.

Note: get(key, default) never modifies the dictionary, even when the key is missing. setdefault(key, default) does modify it, adding the key, whenever it was missing. This is the one real difference between the two.

Explanation

update() is the dictionary equivalent of a list's extend() — both merge one collection's contents into another in a single call, rather than requiring a separate line for each individual pair.

Summary

get(key, default) returns a fallback value without changing the dictionary if key is missing; update(other_dict) merges another dictionary's pairs in, overwriting existing keys and adding new ones; setdefault(key, default) returns an existing value, or adds key with default and returns that, actually changing the dictionary when the key was missing.

Syntax

dict1.update(dict2)

This modifies dict1 in-place. It does not return a new dictionary.

# 1. Using update()
profile = {"Name": "Amit", "Age": 16}
contact = {"Email": "amit@mail.com", "Age": 17} # Note: Age is different!

print("Before Update:", profile)

# Merge 'contact' into 'profile'
# 'Email' is added. 'Age' is overwritten (16 -> 17).
profile.update(contact)

print("After Update:", profile)
# Output: {'Name': 'Amit', 'Age': 17, 'Email': 'amit@mail.com'}
# 2. Using pop() and popitem()
data = {
    "A": 10,
    "B": 20,
    "C": 30
}

# pop(key) - Targeted Removal
val = data.pop("B")
print("Removed Value:", val)      # 20
print("Dict Now:", data)          # {'A': 10, 'C': 30}

# popitem() - Last Item Removal
last_pair = data.popitem()
print("Removed Pair:", last_pair) # ('C', 30) <-- Returns a Tuple!
print("Dict Now:", data)          # {'A': 10}
# 3. Using clear() vs del
cart = {"Apple": 5, "Banana": 12}

# Method A: clear()
cart.clear()
print("Cart is now:", cart) # Output: {} (Empty, but exists)

# Method B: del
del cart
# print(cart) <-- This would cause NameError because 'cart' no longer exists!

Introduction

So far you've reached values one key at a time. This topic covers the three methods that hand back a dictionary's keys, values, or pairs all at once — and a genuinely surprising fact about what they actually return.

Concept

  • keys() — returns all the keys.
  • values() — returns all the values.
  • items() — returns all the key-value pairs, each as a tuple.

All three don't return a plain list — they return a special view object, which stays live: if the dictionary changes afterwards, the same view automatically reflects that change, with no need to call the method again.

Worked Example

marks = {"Physics": 85, "Chemistry": 78}

keys_view = marks.keys()
print(keys_view)

marks["Maths"] = 92
print(keys_view)

print(list(marks.values()))
print(list(marks.items()))

marks.keys() returns dict_keys(['Physics', 'Chemistry']), stored in keys_view. Then marks["Maths"] = 92 adds a new pair to marks — after keys_view was already created. Printing keys_view again shows dict_keys(['Physics', 'Chemistry', 'Maths']) — "Maths" now appears, even though keys_view was never reassigned. list(marks.values()) converts the values view into an ordinary list, [85, 78, 92]; list(marks.items()) gives [('Physics', 85), ('Chemistry', 78), ('Maths', 92)] — a list of key-value tuples.

Note: A view object is "live" — it's not a snapshot taken at the moment keys() was called. Wrapping it in list(), as shown above, does take a genuine, fixed snapshot at that instant; the view itself never stops tracking the dictionary.

Explanation

This live-view behaviour is easy to miss, because in ordinary code it rarely causes a visible problem — but it's an exam-tested fact precisely because it's the one place a dictionary's built-in accessors behave noticeably differently from a list's or tuple's built-in functions, none of which ever "follow" the original collection after being called.

Summary

keys(), values(), and items() each return a live view object linked to the dictionary — one that automatically reflects any later change, unlike a plain list. Wrap any of them in list() to get a fixed, ordinary list instead.

Syntax

dictionary.keys()

Returns: A dict_keys object containing all the keys.
Usage: useful when you only need to check what data exists, not the values.

# 1. Using keys()
student = {"Name": "Rohan", "Age": 17, "Class": 11}

all_keys = student.keys()
print(all_keys)
# Output: dict_keys(['Name', 'Age', 'Class'])

# Iterating over keys
for k in student.keys():
    print("Label:", k)

Syntax

dictionary.values()

Returns: A dict_values object containing all the values.
Note: Since values don't need to be unique, this list might contain duplicates (e.g., two students with the same score).

# 2. Using values()
scores = {"Math": 90, "Physics": 90, "English": 85}

# Notice '90' appears twice
print(scores.values())
# Output: dict_values([90, 90, 85])

Syntax

dictionary.items()

Returns: A dict_items object containing tuples: (key, value).
Usage: The best way to loop through a dictionary if you need both the label and the data.

# 3. Using items()
stock = {"Pen": 10, "Pencil": 50}

print(stock.items())
# Output: dict_items([('Pen', 10), ('Pencil', 50)])
Method Returns Object Content Type Duplicates?
.keys() dict_keys Keys only No (Unique)
.values() dict_values Values only Yes (Allowed)
.items() dict_items Tuples (Key, Value) No (Unique pairs)
# Dynamic View Demonstration
my_dict = {"A": 1}
view = my_dict.values()

print("Original View:", list(view)) # [1]

# Now, we add an item to the dictionary
my_dict["B"] = 2

# We do NOT run .values() again, but the view updates!
print("Updated View:", list(view))  # [1, 2]

Introduction

This closing topic covers looping through a dictionary's contents, and the remaining methods that remove data from one.

Concept

Traversal

A plain for key in my_dict: loop visits every key, one at a time — to reach the matching value inside the loop, use my_dict[key]. Looping over my_dict.items() instead gives both the key and the value together, unpacked directly into two loop variables.

Removing Data

  • pop(key) — removes key and returns its value. Raises KeyError if key is missing.
  • popitem() — removes and returns the most recently added key-value pair, as a tuple.
  • del my_dict[key] — removes key (a statement, doesn't return anything).
  • clear() — removes every pair, leaving the dictionary empty.
  • copy() — returns a shallow copy of the dictionary — a separate dictionary object with the same pairs.

Worked Example

marks = {"Physics": 85, "Chemistry": 78, "Maths": 92}

for key in marks:
    print(key, "->", marks[key])

for subject, score in marks.items():
    print(subject, "scored", score)

removed = marks.pop("Chemistry")
print(removed, marks)

last = marks.popitem()
print(last, marks)

The first loop visits each key ("Physics", "Chemistry", "Maths") and looks up its value with marks[key]. The second loop unpacks marks.items() directly into subject and score, reaching both at once with no separate lookup needed. marks.pop("Chemistry") removes that pair and returns 78; marks becomes {'Physics': 85, 'Maths': 92}. marks.popitem() then removes and returns the most recently added pair — ('Maths', 92) — leaving marks as {'Physics': 85}.

Note: Looping directly over a dictionary (for key in my_dict:) visits keys only, never values — this is the dictionary's default iteration behaviour, and exactly why .items() exists for whenever both are needed together.

Explanation

pop(key) on a dictionary mirrors pop(index) on a list — both remove by identifying a specific location (a key, or an index) and hand back what was removed — while popitem() has no real list equivalent, since a dictionary's "most recently added" pair isn't something a list's plain position-based pop() needs a separate method for.

Summary

for key in my_dict: traverses keys only; for key, value in my_dict.items(): traverses both together. pop(key) removes by key and returns the value; popitem() removes and returns the most recently added pair; del my_dict[key] removes without returning anything; clear() empties the dictionary; copy() returns a separate, independent copy.

Three Ways to Loop

Python allows you to iterate over different parts of the dictionary depending on your need:

  • By Key (Default): for k in d: - Useful for lookups.
  • By Value: for v in d.values(): - Useful for calculations (e.g., sum of prices).
  • By Both (Items): for k, v in d.items(): - Most robust method; gives you full access.
# Traversal 1: Using Keys
capitals = {"India": "New Delhi", "USA": "Washington DC"}

print("--- Country List ---")
for country in capitals:
    # 'country' holds the Key (India, USA)
    print(country)

# Output:
# India
# USA
# Traversal 3: Using Items (Key, Value)
menu = {"Pizza": 200, "Burger": 100}

print("--- Menu ---")
# 'item' gets the Key, 'price' gets the Value
for item, price in menu.items():
    print(item, "costs Rs.", price)

# Output:
# Pizza costs Rs. 200
# Burger costs Rs. 100

Syntax

sorted(dictionary)

Returns: A sorted List of the dictionary's Keys.
To iterate in order, you loop through this sorted list and use the keys to look up values.

# Sorting
data = {"C": 30, "A": 10, "B": 20}

# 1. Get Sorted Keys
sorted_keys = sorted(data)
print("Sorted Keys:", sorted_keys) # ['A', 'B', 'C']

# 2. Loop in Sorted Order
print("--- Ordered Data ---")
for k in sorted(data):
    print(k, ":", data[k])

Syntax

dict.setdefault(key, default_value)

Useful for initializing counters or lists inside a dictionary without checking if key in d: every time.

# setdefault() Example
settings = {"theme": "Dark"}

# Case 1: Key exists -> Returns existing value ("Dark")
val1 = settings.setdefault("theme", "Light")
print("Theme is:", val1) # Output: Dark (No change)

# Case 2: Key missing -> Adds Key with Default Value (50)
val2 = settings.setdefault("volume", 50)
print("Volume is:", val2) # Output: 50
print("Final Dict:", settings)
# Create a blank attendance sheet
students = ["Amit", "Neha", "Rahul"]

# Set everyone to 'Present' by default
attendance = dict.fromkeys(students, "Present")

print(attendance)
# Output: {'Amit': 'Present', 'Neha': 'Present', 'Rahul': 'Present'}

Introduction

This topic puts the whole chapter to work in two small, complete programs — the kind of question this chapter is tested with.

Program 1: Word Frequency Counter

words = ["red", "blue", "red", "green", "blue", "red"]

frequency = {}

for word in words:
    if word in frequency:
        frequency[word] = frequency[word] + 1
    else:
        frequency[word] = 1

print(frequency)

frequency starts empty. For each word, the if word in frequency: check (from the Logical & Membership Operators topic) decides whether this word has been seen before: if it has, its count is increased by 1; if it hasn't, it's added with a starting count of 1. Tracing words in order: red → {'red': 1}, blue → {'red': 1, 'blue': 1}, red again → {'red': 2, 'blue': 1}, green → adds a new key, blue again → count becomes 2, red a third time → count becomes 3. The final result is {'red': 3, 'blue': 2, 'green': 1}.

Program 2: Find the Subject With the Highest Score

marks = {"Physics": 85, "Chemistry": 78, "Maths": 92}

best_subject = None
best_score = -1

for subject, score in marks.items():
    if score > best_score:
        best_subject = subject
        best_score = score

print("Highest scorer:", best_subject, "with", best_score)

best_score starts at -1, lower than any real mark, so the very first pair checked is guaranteed to replace it. The loop then compares every subject's score against the best one found so far, updating best_subject and best_score together whenever a higher score is found — the same running-comparison pattern used to find the highest score in a nested list earlier in this unit, adapted here to .items() pairs. The final result is Highest scorer: Maths with 92.

Tip: Starting best_score at -1 only works because marks are never negative in this problem. A more general version would instead start best_subject/best_score from the loop's very first pair, exactly like the nested-list topper program did.

Explanation

Both programs reuse a pattern from earlier in this unit — a dictionary being built up one key at a time inside a loop (Program 1), and a running "best so far" comparison (Program 2) — showing that a dictionary slots into the same loop-based problem-solving approach as lists and tuples, just keyed by name instead of position.

Summary

A word-frequency counter builds up a dictionary one word at a time, using in to decide whether to start a new count or increase an existing one. Finding the highest-scoring subject traverses .items() while tracking the best pair seen so far — the same running-comparison technique used earlier for lists, applied here to key-value pairs.

Algorithm: The Tally System

  1. Initialize: counts = {}
  2. Loop: for char in text:
  3. Decision:
    • If char exists in counts → counts[char] = counts[char] + 1
    • Else → counts[char] = 1
# Program: Count Character Frequency
text = "banana"
counts = {}

for char in text:
    if char in counts:
        # We have seen this before, add 1
        counts[char] = counts[char] + 1
    else:
        # First time seeing this, set to 1
        counts[char] = 1

print("Frequency Table:")
for key, val in counts.items():
    print(f"'{key}' appears {val} times")

Why use a Dictionary?

If we used two Lists (one for Names, one for Salaries), we would have to search the first list to find the index, then use that index on the second list.

With a Dictionary, we just ask db["Name"] and get the answer instantly. This is much faster.

# Program: Employee Salary Lookup
employees = {
    "Amit": 50000,
    "Neha": 60000,
    "Raj": 45000
}

# 1. Display all data
print("--- Current Employees ---")
for name in employees:
    print(f"- {name}")

# 2. Search Functionality
search_name = input("\nEnter name to check salary: ")

# Use .get() to handle missing names safely
salary = employees.get(search_name)

if salary is None:
    print("Sorry, employee not found.")
else:
    print(f"{search_name}'s Salary is: Rs. {salary}")

Introduction

A working recap of this chapter, tying together creation, safe access, updating, view objects, and traversal in one continuous program.

Concept

  • Create: {key: value, ...} — mutable, unique immutable keys, any-type values.
  • Access: my_dict[key] (raises KeyError if missing) or my_dict.get(key, default) (returns default instead).
  • Add/Update: my_dict[key] = value — adds if new, overwrites if the key already exists; update(other_dict) merges many pairs at once.
  • Remove: pop(key), popitem(), del my_dict[key], clear().
  • View: keys(), values(), items() — live view objects that reflect later changes.
  • Traverse: for key in my_dict: (keys only) or for key, value in my_dict.items(): (both together).

Worked Example

student = {"name": "Aisha", "Physics": 85, "Chemistry": 78}

print(student.get("Maths", "Not Enrolled"))

student["Maths"] = 92
print(student)

subjects = []
for key in student.keys():
    if key != "name":
        subjects.append(key)
print(subjects)

for subject, value in student.items():
    if subject != "name":
        print(subject, ":", value)

student.get("Maths", "Not Enrolled") returns the fallback, since "Maths" isn't a key yet. student["Maths"] = 92 then adds it. The next loop builds subjects by traversing .keys() and appending every key except "name" — giving ['Physics', 'Chemistry', 'Maths']. The final loop then traverses .items(), printing each subject and its score, skipping the "name" pair the same way.

Tip: key != "name" is the same "not equal to" relational operator from the very first operators topic in this unit — dictionaries don't introduce any new comparison logic, only a new way to organise the data being compared.

Summary

A dictionary is created with { }, read safely with get() or directly with [key], updated with the same [key] = syntax used to add new pairs, inspected with the live keys()/values()/items() views, and traversed with for key in my_dict: or for key, value in my_dict.items(): — the full toolkit this chapter covered, working together on one dictionary.

What is a Dictionary?

A Dictionary is a collection of items stored as Key-Value Pairs enclosed in Curly Braces { }. Unlike Lists which use numbered indices (0, 1, 2...), Dictionaries use unique labels (Keys) to access data.

Mutable

You can add, delete, or modify items at any time.

Unordered

Items are not stored by position. (Note: Python 3.7+ preserves insertion order, but logic remains Key-based).

KEYS (The Label) VALUES (The Data)
  • Must be Unique: No duplicates allowed. If repeated, the last one overwrites the previous.
  • Must be Immutable: Strings, Numbers, Tuples.
  • No Lists allowed!
  • Can be Anything: Strings, Lists, other Dictionaries.
  • Duplicates Allowed: Two different keys can have the same value.
Category Method / Syntax Action
Access d["Key"] Get value. Crashes if missing.
d.get("Key") Get value. Returns None if missing (Safe).
Modify d["K"] = V Adds new pair OR Updates existing pair.
d.update(d2) Merges d2 into d. Overwrites duplicates.
d.setdefault(k, v) Returns value if exists; else adds k with v.
Delete d.pop("K") Removes key "K" and returns value.
d.popitem() Removes LAST inserted item. Returns Tuple.
del d["K"] Deletes key "K" (No return).
d.clear() Empties the dictionary (Result: {}).
Views d.keys() Returns list of Keys.
d.values() Returns list of Values.
d.items() Returns list of Tuples (Key, Value).
data = {"Name": "Ali", "Age": 17}
for k in data:
    print(k) # Prints 'Name', 'Age'
data = {"Name": "Ali", "Age": 17}
for v in data.values():
    print(v) # Prints 'Ali', 17
data = {"Name": "Ali", "Age": 17}
for k, v in data.items():
    print(k, "-->", v) # Prints 'Name --> Ali', etc.

Comparison: List vs Dictionary

Feature List [ ] Dictionary { }
Access By Index (Integer)
L[0]
By Key (Label)
D["Name"]
Order Ordered
Position 0, 1, 2 is strictly maintained.
Unordered*
Keys are hashed. (Insertion order preserved in Python 3.7+).
Lookup Speed Slower (Linear Search)
Must scan the list to find an item.
Very Fast (Hash Map)
Instant access regardless of size.
Best For... Sequences of same-type items.
(e.g., Daily Temperatures, To-Do List)
Labeled data records.
(e.g., Student Profile, Product Details)

Want the full course?

Unlock all chapters, video lectures, and SQL practice exercises.

Get Full Access