Checking your session…
Module 03: Data Structures Mastery

Advanced Data Structures

Level: 4 | Version: v1.0 | Author: Meptrasoft

This module builds on Level 3's strings, lists, tuples, sets, and dictionaries. It uses a College Result Management System as the running example — storing and processing student names, marks, and full student records.


Packing & Unpacking

What Gets Packed and Unpacked

Recall from Level 3 that packing combines multiple comma-separated values into a tuple (parentheses optional), and unpacking splits a tuple back into separate names, one value per name — with the name count matching the value count, or Python raises ValueError.

PYTHON
student = ("Arun", 78, "Pass")
name, marks, status = student
print(name, marks, status)

Output:

TEXT
Arun 78 Pass

Unpacking Any Iterable

Unpacking works with any iterable — lists, strings, and the results of functions like zip() — not only tuples. (zip() pairs items from two iterables by position.)

PYTHON
subjects = ["Maths", "Science", "English"]
first, second, third = subjects
print(first, second, third)

Output:

TEXT
Maths Science English
PYTHON
letters = "abc"
a, b, c = letters
print(a, b, c)

Output:

TEXT
a b c

Multiple Assignment

Python allows assigning several names in a single line, using packing and unpacking behind the scenes.

Same Value to Multiple Names

PYTHON
a = b = c = 0
print(a, b, c)

Output:

TEXT
0 0 0

All three names refer to the same value here — useful for initializing counters or totals together.

Different Values in One Line

PYTHON
name, marks, status = "Arun", 78, "Pass"
print(name, marks, status)

Output:

TEXT
Arun 78 Pass

Swapping Values Using Unpacking

A quick recap from Level 3 — swapping two values still uses pack-then- unpack, with no temporary variable needed:

PYTHON
first_marks, second_marks = 78, 92
first_marks, second_marks = second_marks, first_marks
print(first_marks, second_marks)

Output:

TEXT
92 78

Extended Unpacking with *

When the number of values is unknown or larger than the number of names, * collects the "rest" into a list.

PYTHON
marks = [78, 92, 85, 60, 74]
first, *remaining = marks
print(first)
print(remaining)

Output:

TEXT
78
[92, 85, 60, 74]

* can appear at the start, middle, or end — it always collects whatever is left over into a list:

PYTHON
marks = [78, 92, 85, 60, 74]

*rest, last = marks
print(rest, last)

first, *middle, last = marks
print(first, middle, last)

Output:

TEXT
[78, 92, 85, 60] 74
78 [92, 85, 60] 74
TEXT
*name  →  COLLECTS REMAINING VALUES  →  LIST

Use only one starred name in one unpacking assignment.

Unpacking in a for Loop

Unpacking is very common while looping over a list of tuples — for example, student records.

PYTHON
students = [("Arun", 78), ("Divya", 92), ("Karthik", 85)]

for name, marks in students:
    print(name, "scored", marks)

Output:

TEXT
Arun scored 78
Divya scored 92
Karthik scored 85

Each tuple in the list is unpacked into name and marks automatically on every loop iteration.

Ignoring Values with _

When a value from unpacking is not needed, _ is a common convention to mark it as intentionally ignored.

PYTHON
name, _, status = ("Arun", 78, "Pass")
print(name, status)

Output:

TEXT
Arun Pass
TEXT
_  →  CONVENTION FOR "VALUE NOT NEEDED"

Comprehensions

A comprehension builds a new list, set, or dictionary from an existing iterable, in a single readable line.

List Comprehensions

Without a comprehension, building a new list needs a loop and append():

PYTHON
marks = [78, 92, 85, 60, 74]
updated_marks = []

for mark in marks:
    updated_marks.append(mark + 5)

print(updated_marks)

Output:

TEXT
[83, 97, 90, 65, 79]

The same result with a list comprehension:

PYTHON
marks = [78, 92, 85, 60, 74]
updated_marks = [mark + 5 for mark in marks]
print(updated_marks)

Output:

TEXT
[83, 97, 90, 65, 79]
TEXT
[ EXPRESSION  for  ITEM  in  ITERABLE ]

Comprehension with a Condition (Filter)

Adding if at the end keeps only the items that satisfy a condition.

PYTHON
marks = [78, 92, 85, 60, 45]
passed = [mark for mark in marks if mark >= 50]
print(passed)

Output:

TEXT
[78, 92, 85, 60]
TEXT
[ EXPRESSION  for  ITEM  in  ITERABLE  if  CONDITION ]

Comprehension with if...else (Transform Every Item)

An if...else placed before the for transforms every item instead of filtering it out.

PYTHON
marks = [78, 92, 45, 60, 30]
result = ["Pass" if mark >= 50 else "Fail" for mark in marks]
print(result)

Output:

TEXT
['Pass', 'Pass', 'Fail', 'Pass', 'Fail']
Filtering form (if at the end) Transforming form (if...else before for)
Removes items Keeps every item, changes its value
[x for x in data if cond] [a if cond else b for x in data]
Result can be shorter than the input Result is always the same length as the input

List Comprehension vs Loop

Loop with append() List Comprehension
Multiple lines Single line
Needs an empty list created first Builds the list directly
More explicit for complex logic More readable for simple transforms
TEXT
SIMPLE TRANSFORM / FILTER  →  COMPREHENSION
COMPLEX MULTI-STEP LOGIC   →  LOOP

Nested List Comprehensions

A comprehension can contain another for, which is useful for working with a list of lists (for example, marks per subject, per student).

PYTHON
subject_marks = [[78, 92, 85], [60, 74, 88]]

flat_marks = [mark for row in subject_marks for mark in row]
print(flat_marks)

Output:

TEXT
[78, 92, 85, 60, 74, 88]
TEXT
subject_marks:
[ [78, 92, 85],  [60, 74, 88] ]
     ↓  ↓  ↓        ↓  ↓  ↓
   flattened into one list

Reading order matters: the for clauses run left to right, exactly like nested for loops written normally:

PYTHON
subject_marks = [[78, 92, 85], [60, 74, 88]]
flat_marks = []
for row in subject_marks:
    for mark in row:
        flat_marks.append(mark)

Building a Nested List with a Comprehension

A comprehension can also create nested structure, such as a small multiplication-style grid of pass marks:

PYTHON
students = ["Arun", "Divya"]
subjects = ["Maths", "Science"]

grid = [[f"{s}-{sub}" for sub in subjects] for s in students]
print(grid)

Output:

TEXT
[['Arun-Maths', 'Arun-Science'], ['Divya-Maths', 'Divya-Science']]

Readability Caution

Nested comprehensions save lines but can quickly become hard to read. Beyond one level of nesting, a normal loop is usually clearer for interview and placement code:

TEXT
1 LEVEL OF NESTING  →  COMPREHENSION IS FINE
2+ LEVELS / COMPLEX LOGIC  →  PREFER A NORMAL LOOP

Set Comprehensions

A set comprehension builds a set — automatically removing duplicate values — using {} instead of [].

PYTHON
grades = ["A", "B", "A", "C", "B", "A"]
unique_grades = {grade for grade in grades}
print(unique_grades)

Output:

TEXT
{'B', 'A', 'C'}

The displayed order may vary because sets are unordered.

TEXT
{ EXPRESSION  for  ITEM  in  ITERABLE }   →  {} means SET

Set comprehensions are useful for collecting unique subjects, unique roll numbers, or unique grades from repeated records:

PYTHON
records = [("Arun", "Maths"), ("Divya", "Science"), ("Karthik", "Maths")]
subjects_offered = {subject for name, subject in records}
print(subjects_offered)

Output:

TEXT
{'Science', 'Maths'}
List Comprehension Set Comprehension
Uses [] Uses {}
Result is a list Result is a set
Duplicates are kept Duplicates are removed automatically
Order is preserved Order is not guaranteed

Dictionary Comprehensions

A dictionary comprehension builds a dictionary using {key: value for ...}.

zip() pairs the two lists by position, as introduced earlier in this module.

PYTHON
students = ["Arun", "Divya", "Karthik"]
marks = [78, 92, 85]

result = {name: mark for name, mark in zip(students, marks)}
print(result)

Output:

TEXT
{'Arun': 78, 'Divya': 92, 'Karthik': 85}
TEXT
{ KEY : VALUE  for  ITEM  in  ITERABLE }

Filtering a Dictionary Comprehension

PYTHON
marks = {"Arun": 78, "Divya": 45, "Karthik": 85, "Priya": 40}
passed = {name: mark for name, mark in marks.items() if mark >= 50}
print(passed)

Output:

TEXT
{'Arun': 78, 'Karthik': 85}

Transforming Keys or Values

PYTHON
marks = {"Arun": 78, "Divya": 92, "Karthik": 85}
result_status = {name: ("Pass" if mark >= 50 else "Fail") for name, mark in marks.items()}
print(result_status)

Output:

TEXT
{'Arun': 'Pass', 'Divya': 'Pass', 'Karthik': 'Pass'}

Swapping Keys and Values

PYTHON
roll_numbers = {"Arun": 101, "Divya": 102, "Karthik": 103}
swapped = {roll: name for name, roll in roll_numbers.items()}
print(swapped)

Output:

TEXT
{101: 'Arun', 102: 'Divya', 103: 'Karthik'}

Comprehension Types Compared

Type Brackets Builds Duplicates
List [] list Kept
Set {} set Removed
Dictionary {k: v} dict Keys overwrite
TEXT
[]        →  LIST
{item}    →  SET
{k: v}    →  DICTIONARY

Nested Structures

Why Nest Data Structures

A nested data structure is a data structure that contains other data structures inside it — for example, a list of tuples, a list of dictionaries, or a dictionary of lists. Real applications almost always store data this way, because a single student, order, or record naturally has several related fields.

List of Tuples

PYTHON
students = [("Arun", 78), ("Divya", 92), ("Karthik", 85)]
print(students[0])
print(students[0][0])
print(students[0][1])

Output:

TEXT
('Arun', 78)
Arun
78

List of Dictionaries

Each student is a dictionary; all students together form a list — this is the most common way real systems represent a table of records.

PYTHON
students = [
    {"name": "Arun", "marks": 78, "status": "Pass"},
    {"name": "Divya", "marks": 92, "status": "Pass"},
    {"name": "Karthik", "marks": 40, "status": "Fail"},
]

for student in students:
    print(student["name"], "-", student["marks"], "-", student["status"])

Output:

TEXT
Arun - 78 - Pass
Divya - 92 - Pass
Karthik - 40 - Fail
TEXT
[ {...}, {...}, {...} ]   →   LIST OF DICTIONARIES  →  TABLE OF RECORDS

Dictionary of Lists

All marks for each subject, grouped under one dictionary:

PYTHON
subject_marks = {
    "Maths": [78, 92, 85],
    "Science": [60, 74, 88],
}

print(subject_marks["Maths"])
print(subject_marks["Maths"][0])

Output:

TEXT
[78, 92, 85]
78

Dictionary of Dictionaries

Each student's full record, keyed by name:

PYTHON
students = {
    "Arun": {"marks": 78, "status": "Pass"},
    "Divya": {"marks": 92, "status": "Pass"},
}

print(students["Arun"])
print(students["Arun"]["marks"])

Output:

TEXT
{'marks': 78, 'status': 'Pass'}
78
TEXT
DICT { NAME : DICT { FIELD : VALUE } }

Accessing Nested Values

Each level is accessed with one more [] step, moving from the outer structure to the inner value.

PYTHON
college = {
    "students": [
        {"name": "Arun", "marks": [78, 92, 85]},
        {"name": "Divya", "marks": [60, 74, 88]},
    ]
}

print(college["students"][0]["name"])
print(college["students"][0]["marks"][1])

Output:

TEXT
Arun
92
TEXT
college["students"][0]["marks"][1]
  ↑          ↑        ↑       ↑
 dict       list     dict    list

Modifying Nested Values

Navigating to the exact nested position also lets a value be updated in place.

PYTHON
college = {
    "students": [
        {"name": "Arun", "marks": [78, 92, 85]},
    ]
}

college["students"][0]["marks"][0] = 80
print(college["students"][0]["marks"])

Output:

TEXT
[80, 92, 85]

Shallow Copy vs Deep Copy of Nested Data

copy() on a dictionary or list only copies the outer structure — any lists or dictionaries nested inside it are still shared between the original and the copy.

PYTHON
college = {
    "students": [
        {"name": "Arun", "marks": [78, 92, 85]},
    ]
}

college_copy = college.copy()
college_copy["students"][0]["marks"][0] = 100

print(college["students"][0]["marks"])

Output:

TEXT
[100, 92, 85]

Even though college_copy is a separate dictionary, its inner student dictionary and marks list are the same objects as in college — this is called a shallow copy. To copy every nested level independently, use copy.deepcopy():

PYTHON
college = {
import copy

college_deep = copy.deepcopy(college)
college_deep["students"][0]["marks"][0] = 999

print(college["students"][0]["marks"])

Output:

TEXT
[100, 92, 85]
TEXT
copy()          →  copies only the OUTER structure   →  SHALLOW COPY
copy.deepcopy() →  copies EVERY nested level          →  DEEP COPY

Real-World Example: Building a Result Report

Combining comprehensions with nested structures is common in real projects — for example, generating a pass/fail report from a list of student records.

PYTHON
students = [
    {"name": "Arun", "marks": 78},
    {"name": "Divya", "marks": 45},
    {"name": "Karthik", "marks": 85},
]

report = {student["name"]: ("Pass" if student["marks"] >= 50 else "Fail") for student in students}
print(report)

Output:

TEXT
{'Arun': 'Pass', 'Divya': 'Fail', 'Karthik': 'Pass'}
TEXT
LIST OF DICTS  →  DICT COMPREHENSION  →  NAME : RESULT

Common Fresher Mistakes

Mistake Problem Correct Understanding
Unpacking with the wrong number of names Raises ValueError: too many/not enough values to unpack The name count must match the value count
Forgetting * collects the "rest" into a list, not a tuple Code that assumes a tuple result behaves unexpectedly *rest always produces a list
Trying to swap values with a temporary variable out of habit Adds an unnecessary extra variable and line Python can swap directly: a, b = b, a
Writing a comprehension that is hard to read instead of a normal loop Reduces readability and makes debugging harder Prefer a loop when logic has more than one condition or nesting level
Confusing [], {item}, and {k: v} comprehension syntax Builds the wrong collection type by accident [] → list, {item} → set, {k: v} → dictionary
Assuming a set comprehension preserves order or duplicates Code relying on order or duplicate counts breaks silently Sets remove duplicates and do not guarantee order
Forgetting keys overwrite in a dictionary comprehension Earlier values for a repeated key are silently lost A repeated key keeps only the last value seen
Accessing a nested value with too few or wrong [] steps Raises KeyError, IndexError, or TypeError Each nesting level needs its own [] step
Modifying a nested structure and expecting the original data unaffected Unexpected side effects appear because the reference is shared Nested lists/dicts are shared references; changes are visible everywhere
Using deeply nested comprehensions "to look advanced" Makes code hard to read, debug, and review Clarity matters more than compressing code into one line

Placement Quick Points

TEXT
PACKING     → Multiple values combined into one object (tuple)
UNPACKING   → Splitting an iterable into separate names
*rest       → Collects remaining values into a LIST
SWAP        → a, b = b, a  (no temporary variable needed)
LIST COMP   → [expr for item in iterable]
FILTER COMP → [expr for item in iterable if condition]
TRANSFORM   → [a if cond else b for item in iterable]
SET COMP    → {expr for item in iterable}  → removes duplicates
DICT COMP   → {key: value for item in iterable}
NESTED DATA → structure containing another structure (list of dicts, dict of lists, etc.)
ACCESS      → one [] per nesting level

Interview Questions

What is packing in Python?
Packing is combining multiple comma-separated values into a single object, usually a tuple.
What is unpacking in Python?
Unpacking is splitting an iterable into separate names, one value per name.
What happens if the number of names does not match the number of values while unpacking?
Python raises a ValueError.
Can unpacking be used with lists and strings, not just tuples?
Yes. Unpacking works with any iterable.
How does Python swap two values without a temporary variable?
Using packing and unpacking together: a, b = b, a.
What does * do during unpacking?
It collects the remaining values into a list; it can appear at the start, middle, or end.
What type of object does *rest produce?
A list.
What is the underscore _ used for during unpacking?
It is a convention to indicate that a value from unpacking is intentionally ignored.
What is a list comprehension?
A concise way to build a new list from an iterable in a single line, in the form [expression for item in iterable].
How is filtering added to a list comprehension?
By adding if condition at the end of the comprehension.
How is a transforming if...else written in a comprehension, and how is it different from a filtering if?
It is written before the for clause, as [a if condition else b for item in iterable]. It keeps every item and changes its value, while a filtering if at the end removes items instead.
What is a nested list comprehension?
A comprehension that contains another for clause, commonly used to flatten a list of lists.
When should a normal loop be preferred over a comprehension?
When the logic involves more than one condition or more than one level of nesting, since readability matters more than compressing lines.
What is a set comprehension and how is its syntax different from a list comprehension?
A set comprehension builds a set using {expression for item in iterable}; unlike a list comprehension, duplicate values are automatically removed.
What is a dictionary comprehension?
A concise way to build a dictionary using {key: value for item in iterable}.
What happens when a dictionary comprehension produces a repeated key?
The last value seen for that key is kept; earlier values are overwritten.
What is a nested data structure?
A data structure that contains other data structures inside it, such as a list of dictionaries or a dictionary of lists.
Give an example of a common nested structure used to represent multiple records.
A list of dictionaries, where each dictionary represents one record (for example, one student).
How many [] steps are needed to access a value nested three levels deep?
Three — one step per nesting level.

Practice

Use a College Result Management System.

  1. Pack a student's name, marks, and status into a tuple in one line.
  2. Unpack that tuple into three separate names.
  3. Try unpacking into the wrong number of names and observe the error.
  4. Unpack a string into individual character names.
  5. Assign the same value 0 to three names in one line.
  6. Assign three different values to three names in one line.
  7. Swap two mark values without using a temporary variable.
  8. Use *rest to collect all marks except the first from a list.
  9. Use *rest to collect all marks except the last from a list.
  10. Loop over a list of (name, marks) tuples using unpacking.
  11. Use _ to ignore the status field while unpacking a student record.
  12. Write a list comprehension that adds 5 bonus marks to every value in a list.
  13. Write a list comprehension that keeps only marks greater than or equal to 50.
  14. Write a list comprehension that labels each mark as "Pass" or "Fail".
  15. Flatten a list of subject-wise mark lists using a nested list comprehension.
  16. Build a small nested list pairing student names with subject names.
  17. Write a set comprehension that collects unique grades from a list.
  18. Write a set comprehension that collects unique subjects from a list of records.
  19. Write a dictionary comprehension mapping student names to marks using zip().
  20. Write a dictionary comprehension that keeps only students who passed.
  21. Write a dictionary comprehension that swaps keys and values of a roll-number dictionary.
  22. Create a list of dictionaries, each representing one student's record.
  23. Create a dictionary of lists, grouping marks by subject.
  24. Create a dictionary of dictionaries, keyed by student name.
  25. Access a specific student's marks from a nested list of dicts inside a dict.
  26. Modify one nested mark value in place and print the updated structure.
  27. Build a pass/fail report dictionary from a list of student-record dictionaries using a dictionary comprehension.

Predict the output:

For set outputs, the order may vary.

PYTHON
name, marks = "Arun", 78
print(name, marks)
PYTHON
a, *b = [78, 92, 85, 60]
print(a)
print(b)
PYTHON
marks = [78, 45, 92, 30]
result = [m for m in marks if m >= 50]
print(result)
PYTHON
grades = ["A", "B", "A", "C"]
print({g for g in grades})
PYTHON
students = {"Arun": 78, "Divya": 92}
print({v: k for k, v in students.items()})

Find the mistake:

PYTHON
name, marks, status = ("Arun", 78)
PYTHON
students = [{"name": "Arun", "marks": [78, 92]}]
print(students["name"])
PYTHON
marks = {"Arun": 78, "Arun": 92}
print(marks)

Final task: Build a small College Result Management System that stores several students as a list of dictionaries (name, marks), then use comprehensions to (a) produce a list of only the passing students' names, (b) produce a set of unique pass/fail statuses, and (c) produce a dictionary mapping each student's name to their result status.

Output:

TEXT
PACK / UNPACK  →  MOVE VALUES IN AND OUT OF NAMES
COMPREHENSION  →  BUILD A NEW COLLECTION IN ONE READABLE LINE
NESTED DATA    →  STRUCTURE INSIDE STRUCTURE, ONE [] PER LEVEL

End of Level 4