Python for Data 2: Lists, Tuples, Dictionaries and Sets
These are the notes for Data Analytics with Python, episode 5. You will learn Python's four built-in data structures (ways to organise many values together): lists, tuples, dictionaries and sets. You'll index and slice lists, use list methods (append, extend, pop, sort), learn the difference between mutable and immutable, look things up by key in a dict, remove duplicates with a set, test membership with in, and see how a list of dicts is really a table. Every term is defined the first time it appears and collected in the Glossary. Every program below was run for real (Python 3.13); outputs are unedited.
The problem: one variable per day
Ramesh wants his weekly total and average. With one variable per day:
mon = 320
tue = 280
wed = 410
thu = 300
fri = 350
sat = 390
sun = 450
total = mon + tue + wed + thu + fri + sat + sun
print(total)
print(total / 7)
2500
357.14285714285717
It works for a week. For a year it needs 365 variables and a 365-name total line, and the hand-typed 7 must change whenever a day is added. Miss one and the answer is silently wrong.
Lists
| A train | A Python list | Meaning |
|---|---|---|
| One train, one name, many coaches in order | List | One variable holding many values in order, inside [ ], separated by commas |
| Coaches numbered from the engine, starting at 0 | Index | The position of a value, counted from 0 |
| Attaching or removing coaches | Mutable | Can be changed after it is created |
week = [320, 280, 410, 300, 350, 390, 450]
print(week)
print(type(week))
print(len(week))
print(sum(week))
print(sum(week) / len(week))
print(max(week), min(week))
[320, 280, 410, 300, 350, 390, 450]
<class 'list'>
7
2500
357.14285714285717
450 280
len() (length: how many items), sum(), max() and min() are built-in functions that work on any list. sum(week) / len(week) has no hand-typed 7, so it stays right when days are added. Each value in a list is called an element or item.
Indexing and slicing
Indexing means picking one value by its position: week[0]. A negative index counts from the end: -1 is the last value. A slice week[a:b] is a new, smaller list from index a up to, but not including, b.
week = [320, 280, 410, 300, 350, 390, 450]
index: 0 1 2 3 4 5 6
negative: -7 -6 -5 -4 -3 -2 -1
week = [320, 280, 410, 300, 350, 390, 450]
print(week[0])
print(week[6])
print(week[-1])
print(week[0:5])
print(week[5:])
print(week[7])
320
450
450
[320, 280, 410, 300, 350]
[390, 450]
Traceback (most recent call last):
File "main.py", line 7, in <module>
print(week[7])
~~~~^^^
IndexError: list index out of range
- Leaving out the start (
week[:5]) means "from the beginning"; leaving out the end (week[5:]) means "to the end". - Seven values have indexes 0 to 6; the last valid index is always
len(week) - 1. Going one past the end is called an off-by-one error. - A third number is a step:
week[::2]takes every second value.
List methods
A method is a function that belongs to a value and is called with a dot: week.append(330).
| Method / operation | What it does |
|---|---|
week[1] = 290 |
Replace the value at index 1 (item assignment) |
week.append(x) |
Add one value at the end |
week.extend([a, b]) |
Add every value from another list at the end |
week.insert(i, x) |
Insert a value at position i |
week.pop() |
Remove the last value and return it (pop(i) removes index i) |
week.sort() |
Sort the list in place (changes it, returns None) |
sorted(week) |
Built-in: return a new sorted list, original untouched |
week.index(x) |
The index of the first x |
week = [320, 280, 410, 300, 350, 390, 450]
week[1] = 290 # fix a typo
week.append(330) # next Monday
print(week)
week.extend([310, 295])
print(week, len(week))
last = week.pop()
print(last, week)
week.sort()
print(week)
[320, 290, 410, 300, 350, 390, 450, 330]
[320, 290, 410, 300, 350, 390, 450, 330, 310, 295] 10
295 [320, 290, 410, 300, 350, 390, 450, 330, 310]
[290, 300, 310, 320, 330, 350, 390, 410, 450]
The sort trap
week = [320, 280, 410]
week = week.sort()
print(week)
print(sum(week))
None
Traceback (most recent call last):
File "main.py", line 4, in <module>
print(sum(week))
~~~^^^^^^
TypeError: 'NoneType' object is not iterable
sort() changes the list in place and returns None (nothing), so week = week.sort() replaced the list with None. Iterable means "something you can go through one item at a time"; None isn't. Fix: call week.sort() on its own, or use sorted():
week = [320, 280, 410]
print(sorted(week))
print(week)
print(sorted(week, reverse=True))
[280, 320, 410]
[320, 280, 410]
[410, 320, 280]
Tuples: records that must not change
A tuple holds values in order, written in round brackets ( ), and can never be changed once made. Something that can't change after it's created is immutable.
record = ("Mon", "sunny", 320)
print(record[0], record[2])
print(type(record))
record[2] = 330
Mon 320
<class 'tuple'>
Traceback (most recent call last):
File "main.py", line 4, in <module>
record[2] = 330
~~~~~~^^^
TypeError: 'tuple' object does not support item assignment
| Mutable (can change) | Immutable (can't change) |
|---|---|
list, dict, set |
int, float, str, bool, None, tuple |
Use a tuple for a fixed record (day, weather, cups) and a list for a collection that grows. A one-item tuple needs a comma: (320,).
Dictionaries: look up by name
Two parallel lists (lists that must stay in step) drift apart silently:
items = ["chai", "samosa", "bun maska"]
prices = [12, 15, 25]
print(prices[items.index("samosa")])
items.insert(0, "coffee") # new item added at the top...
print(prices[items.index("samosa")]) # ...prices forgot
15
25
The samosa now "costs" Rs 25. Nothing warned us.
| Menu card | Python | Meaning |
|---|---|---|
| Item name | Key | The name you look up; keys are unique |
| Its price | Value | What the key points to |
| The whole card | dict (dictionary) | Key-value pairs in { }, key: value |
A key and its value form a key-value pair; they travel together, so they can't slide apart. Since Python 3.7, a dict keeps its pairs in the order you added them (the docs: "Dict preserves insertion order").
menu = {"chai": 12, "samosa": 15, "bun maska": 25}
print(menu["samosa"])
menu["coffee"] = 20 # add
menu["chai"] = 15 # update
print(menu)
print(len(menu))
print(menu.keys())
print(menu.values())
print(menu.items())
print(menu.get("vada pav", 0))
print(menu["vada pav"])
15
{'chai': 15, 'samosa': 15, 'bun maska': 25, 'coffee': 20}
4
dict_keys(['chai', 'samosa', 'bun maska', 'coffee'])
dict_values([15, 15, 25, 20])
dict_items([('chai', 15), ('samosa', 15), ('bun maska', 25), ('coffee', 20)])
0
Traceback (most recent call last):
File "main.py", line 11, in <module>
print(menu["vada pav"])
~~~~^^^^^^^^^^^^
KeyError: 'vada pav'
d[key] = valueadds a new pair or updates an existing one.keys(),values()anditems()give the names, the values, and the pairs (as tuples).d[key]raisesKeyErrorfor a missing key;d.get(key, default)returns the default instead.del menu["coffee"]ormenu.pop("coffee")removes a pair.
Sets: unique values
visits = ["Asha", "Ravi", "Asha", "Meena", "Ravi", "Asha", "Joseph"]
print(len(visits))
regulars = set(visits)
print(regulars)
print(len(regulars))
print("Meena" in regulars, "Kiran" in regulars)
regulars.add("Kiran")
print(len(regulars))
print(regulars[0])
7
{'Meena', 'Joseph', 'Ravi', 'Asha'}
4
True False
5
Traceback (most recent call last):
File "main.py", line 9, in <module>
print(regulars[0])
~~~~~~~~^^^
TypeError: 'set' object is not subscriptable
- A set keeps each value once (no duplicates, i.e. repeated values) and has no order: the printed order can differ from run to run.
set(list)removes duplicates;len(set(...))counts unique values (the "distinct count" analysts use constantly).- Subscriptable means you can use
[ ]on it. Sets have no positions, so no indexing. - Write a set with braces:
{"Asha", "Ravi"}. An empty set isset()(because{}is an empty dict). - Set maths:
a & b(in both),a | b(in either),a - b(in a but not b).
Membership with in
x in collection asks "is x inside?" and gives a bool. Membership = being a member of a collection.
week = [320, 280, 410, 300, 350, 390, 450]
menu = {"chai": 12, "samosa": 15}
print(450 in week)
print("samosa" in menu)
print(12 in menu)
print("Mon" in "Monday")
True
True
False
True
in on a dict checks the keys, not the values (12 in menu.values() would be True). On a string it checks for a piece of text. not in is the opposite.
Which structure when?
| Structure | Syntax | Ordered? | Changeable? | Duplicates? | Use it for |
|---|---|---|---|---|---|
| list | [320, 280] |
Yes | Yes (mutable) | Yes | A growing collection: daily sales |
| tuple | ("Mon", 320) |
Yes | No (immutable) | Yes | A fixed record |
| dict | {"chai": 12} |
Yes (insertion order) | Yes | Keys: no | Lookup by name |
| set | {"Asha", "Ravi"} |
No | Yes | No | Unique values, fast in |
Nested data: a list of dicts is a table
Nested data means data structures inside each other.
week = [
{"day": "Mon", "weather": "sunny", "cups": 320},
{"day": "Tue", "weather": "rainy", "cups": 280},
{"day": "Wed", "weather": "sunny", "cups": 410},
]
print(len(week))
print(week[1])
print(week[1]["cups"])
week[1]["cups"] = 290
print(week[1])
3
{'day': 'Tue', 'weather': 'rainy', 'cups': 280}
280
{'day': 'Tue', 'weather': 'rainy', 'cups': 290}
The outer list holds the rows; each dict is one row; the shared keys are the columns. Read week[1]["cups"] left to right: row 1, then column cups. This is exactly how JSON data looks, and pandas turns it into a table with pd.DataFrame(week).
Mini project: weekly summary
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
cups = [320, 280, 410, 300, 350, 390, 450]
rainy = {"Tue", "Thu"}
best = max(cups)
best_day = days[cups.index(best)]
weekend = cups[5:]
print(f"Total: {sum(cups)} cups")
print(f"Average: {sum(cups) / len(cups):.1f} cups a day")
print(f"Best day: {best_day} ({best} cups)")
print(f"Weekend share: {sum(weekend) / sum(cups):.0%}")
print(f"Was Tue rainy? {'Tue' in rainy}")
Total: 2500 cups
Average: 357.1 cups a day
Best day: Sun (450 cups)
Weekend share: 34%
Was Tue rainy? True
:.0% formats a fraction (0.336) as a percentage with no decimals (34%).
Common mistakes
| Code | Real error | Why | Fix |
|---|---|---|---|
week[7] (7 items) |
IndexError: list index out of range |
Indexes run 0 to len - 1 |
week[6] or week[-1] |
week = week.sort() |
None, then TypeError: 'NoneType' object is not iterable |
sort() returns None |
week.sort() or sorted(week) |
record[2] = 330 (tuple) |
TypeError: 'tuple' object does not support item assignment |
Tuples are immutable | Build a new tuple, or use a list |
menu["vada pav"] |
KeyError: 'vada pav' |
The key doesn't exist | menu.get("vada pav", 0) or check in first |
regulars[0] (set) |
TypeError: 'set' object is not subscriptable |
Sets have no order | Loop over it, or sorted(regulars)[0] |
| Parallel lists | (no error: wrong price) | Lists slid out of step | Use a dict |
12 in menu |
(no error: False) |
in checks dict keys |
12 in menu.values() |
Practice
1. sales = [320, 280, 410, 300, 350, 390, 450]. Write the slice for Tuesday to Thursday, and for the last three days, two ways.
Solution
sales[1:4] → [280, 410, 300]. Last three: sales[4:] or sales[-3:] → [350, 390, 450].
2. Build a dict of 5 friends and their cities. Add one friend, change one city, and print each pair.
Solution
cities = {"Asha": "Pune", "Ravi": "Delhi", "Meena": "Chennai", "Joseph": "Kochi", "Kiran": "Jaipur"}
cities["Arjun"] = "Lucknow"
cities["Ravi"] = "Noida"
print(cities.items())
3. Count the unique words in "chai and samosa and chai again".
Solution
words = "chai and samosa and chai again".split() gives a list of 6 words; len(set(words)) is 4 (chai, and, samosa, again). The split() method breaks text into a list at spaces.
4. Which structure would you use for: (a) daily temperatures for a year, (b) a customer's (id, name, city) record, (c) product → stock count, (d) the PIN codes you deliver to?
Solution
(a) list, (b) tuple, (c) dict, (d) set.
5. Given the list-of-dicts week above, print Wednesday's weather, and change Monday's cups to 330.
Solution
print(week[2]["weather"]) → sunny; week[0]["cups"] = 330.
Quick quiz
sales = [320, 280, 410, 300, 350]
print(sales[1:3], sales[-2])
A. [320, 280, 410] 350 B. [280, 410] 300 C. [280, 410, 300] 300 D. [280, 410] 350
Answer
B. The slice starts at index 1 and stops before index 3: [280, 410]. -2 is the second from the end: 300.
Interview questions
- List vs tuple? Both are ordered; a list is mutable, a tuple is immutable (safer for fixed records, usable as dict keys).
- When would you use a set? To remove duplicates, count distinct values, or test membership quickly.
- How do you avoid a KeyError? Use
d.get(key, default)or checkkey in dfirst. sort()vssorted()?list.sort()sorts in place and returnsNone;sorted()returns a new list and works on any iterable.- What does
week[-1]mean? The last element; negative indexes count from the end. - How would you represent a small table in plain Python? A list of dicts (one dict per row, keys as columns), which is also how JSON records look.
Glossary
| Term | Meaning | Example |
|---|---|---|
| Data structure | A way to organise many values together | list, dict |
| List | Ordered, changeable collection in [ ] |
[320, 280] |
| Element / item | One value inside a collection | 280 |
| Index | The position of an element, from 0 | week[0] |
| Negative index | Position counted from the end, from -1 | week[-1] |
| Indexing | Picking one value by position | week[2] |
| Slicing | Taking a sub-list a:b (b excluded) |
week[0:5] |
| Off-by-one error | Going one position too far (or short) | week[7] for 7 items |
| len() | Number of items | len(week) → 7 |
| sum(), max(), min() | Total, largest, smallest | max(week) |
| Built-in function | A function Python always has, no import needed | len, sorted |
| Method | A function attached to a value, called with a dot | week.append(330) |
| append | Add one item at the end | week.append(330) |
| extend | Add all items of another list | week.extend([310, 295]) |
| pop | Remove and return an item (last by default) | week.pop() |
| sort | Sort a list in place; returns None |
week.sort() |
| sorted() | Return a new sorted list | sorted(week, reverse=True) |
| In place | Changing the original object instead of making a new one | week.sort() |
| Iterable | Something you can go through one item at a time | lists, strings, dicts |
| Mutable | Can be changed after creation | list, dict, set |
| Immutable | Can't be changed after creation | tuple, str, int |
| Tuple | Ordered, unchangeable collection in ( ) |
("Mon", 320) |
| Item assignment | Replacing a value at an index or key | week[1] = 290 |
| Parallel lists | Separate lists that must stay in step | items, prices |
| Dictionary (dict) | Key-value pairs in { } |
{"chai": 12} |
| Key | The unique name you look up in a dict | "chai" |
| Value | What a key points to | 12 |
| Key-value pair | One key with its value | "chai": 12 |
| keys() / values() / items() | All keys / all values / all pairs | menu.items() |
| get() | Look up a key with a default if missing | menu.get("x", 0) |
| KeyError | Error for a missing dict key | menu["vada pav"] |
| Set | Unordered collection of unique values | {"Asha", "Ravi"} |
| Duplicate | A value that appears more than once | "Asha" ×3 |
| Subscriptable | Can be used with [ ] |
lists yes, sets no |
Membership (in) |
Test whether a value is inside a collection | "Asha" in regulars |
| Nested data | Data structures inside each other | a list of dicts |
| JSON | A text format for data that looks like dicts and lists | {"day": "Mon"} |
| split() | Break text into a list of words | "a b".split() → ['a', 'b'] |
Summary
- Lists hold many values in order; index from 0, negative from -1, slices stop before the end index.
- Lists are mutable:
append,extend,pop,sort(in place; returnsNone) andsorted(). - Tuples are immutable records.
- Dicts look up values by key; use
.get()for keys that may be missing. - Sets keep unique values with no order.
intests membership; a list of dicts is a table.- Next episode: Python for data, part 3: conditions, loops and comprehensions.