Python Zero to Advanced #5: Lists, Tuples, Dictionaries, Sets & Comprehensions
Welcome to the intermediate block. Almost every Python program manipulates groups of values: a list of orders, a mapping from user ID to profile, a set of unique tags. Python ships with four core collection types, and knowing when to use each one is a big step from "it works" to "it is clean and fast".
| Type | Syntax | Ordered | Mutable | Duplicates |
|---|---|---|---|---|
list | [1, 2] | Yes | Yes | Yes |
tuple | (1, 2) | Yes | No | Yes |
dict | {"a": 1} | Yes (3.7+) | Yes | Keys must be unique |
set | {1, 2} | No | Yes | No |
Lists
A list is an ordered, mutable sequence. You can index it (starting at 0), use negative indexes to count from the end, and slice it with [start:stop:step].
fruits = ["apple", "banana", "cherry", "durian", "mango"]
print(fruits[0], fruits[-1])
print(fruits[1:3])
print(fruits[:2], fruits[3:])
print(fruits[::2])
print(fruits[::-1])
print(len(fruits), "durian" in fruits)
Output:
apple mango
['banana', 'cherry']
['apple', 'banana'] ['durian', 'mango']
['apple', 'cherry', 'mango']
['mango', 'durian', 'cherry', 'banana', 'apple']
5 True
Lists come with many methods. The most common ones modify the list in place and return None.
scores = [70, 85, 60]
scores.append(90) # add to the end
scores.insert(0, 100) # insert at index 0
scores.extend([55, 75]) # add many
print(scores)
last = scores.pop() # remove and return last item
scores.remove(60) # remove first matching value
print(last, scores)
scores.sort()
print(scores)
print(sorted(scores, reverse=True)) # new list, original unchanged
print(scores.index(85), scores.count(70))
Output:
[100, 70, 85, 60, 90, 55, 75]
75 [100, 70, 85, 90, 55]
[55, 70, 85, 90, 100]
[100, 90, 85, 70, 55]
2 1
References vs copies
Assigning a list to another variable does not copy it; both names point to the same object.
a = [1, 2, 3]
b = a # same list
c = a.copy() # shallow copy (also: a[:] or list(a))
a.append(4)
print(a, b, c)
print(a is b, a is c)
Output:
[1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3]
True False
Tuples
A tuple is like a list that cannot be changed after creation. Use tuples for fixed records (coordinates, RGB colours, rows from a database) and when you need a hashable value, for example as a dict key. Tuples shine with unpacking.
point = (3, 4)
x, y = point
print(x, y)
single = (42,) # the comma makes it a tuple
print(type(single), type((42)))
first, *rest = (1, 2, 3, 4)
print(first, rest)
record = ("Budi", "budi@example.com", 28)
name, email, _ = record
print(name, email)
Output:
3 4
<class 'tuple'> <class 'int'>
1 [2, 3, 4]
Budi budi@example.com
Dictionaries
A dictionary maps unique keys to values with very fast lookup. Since Python 3.7, dicts preserve insertion order.
user = {"name": "Siti", "age": 30, "city": "Bandung"}
print(user["name"])
print(user.get("phone")) # None instead of KeyError
print(user.get("phone", "-")) # with a default
user["age"] = 31 # update
user["email"] = "siti@example.com" # insert
del user["city"]
print(user)
for key, value in user.items():
print(f"{key:>6}: {value}")
print(list(user.keys()))
print("email" in user)
Output:
Siti
None
-
{'name': 'Siti', 'age': 31, 'email': 'siti@example.com'}
name: Siti
age: 31
email: siti@example.com
['name', 'age', 'email']
True
Two patterns you will use constantly: counting with get(), and merging dicts with the | operator (Python 3.9+).
words = "the cat and the dog and the bird".split()
counts = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
print(counts)
defaults = {"theme": "light", "lang": "en"}
prefs = {"lang": "id"}
print(defaults | prefs)
Output:
{'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'bird': 1}
{'theme': 'light', 'lang': 'id'}
Sets
A set is an unordered collection of unique, hashable items. Use it to remove duplicates and for fast membership tests. Set algebra (union, intersection, difference) is built in. Because sets are unordered, we wrap them in sorted() below to get a predictable output.
tags = ["python", "web", "python", "api", "web"]
unique = set(tags)
print(sorted(unique), len(unique))
backend = {"python", "go", "java"}
frontend = {"javascript", "typescript", "python"}
print(sorted(backend | frontend)) # union
print(sorted(backend & frontend)) # intersection
print(sorted(backend - frontend)) # difference
seen = set()
seen.add(3)
seen.add(3)
print(seen, 3 in seen)
print(type({})) # {} is an empty dict, not a set
Output:
['api', 'python', 'web'] 3
['go', 'java', 'javascript', 'python', 'typescript']
['python']
['go', 'java']
{3} True
<class 'dict'>
Comprehensions
A comprehension builds a new collection from an iterable in one readable expression: [expression for item in iterable if condition]. The same idea works for dicts and sets.
nums = range(1, 11)
squares = [n * n for n in nums]
evens = [n for n in nums if n % 2 == 0]
labels = ["even" if n % 2 == 0 else "odd" for n in range(1, 5)]
print(squares)
print(evens)
print(labels)
prices = {"coffee": 18000, "tea": 12000, "cake": 25000}
discounted = {k: int(v * 0.9) for k, v in prices.items() if v > 15000}
print(discounted)
lengths = {len(w) for w in ["hi", "hey", "yo", "hello"]}
print(sorted(lengths))
matrix = [[1, 2, 3], [4, 5, 6]]
flat = [x for row in matrix for x in row]
print(flat)
Output:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[2, 4, 6, 8, 10]
['odd', 'even', 'odd', 'even']
{'coffee': 16200, 'cake': 22500}
[2, 3, 5]
[1, 2, 3, 4, 5, 6]
Comprehensions are usually faster than the equivalent for + append loop, but do not cram complex logic into one line. If it needs two conditions and nested loops, a normal loop is more readable.
Common mistakes
x = x.sort()setsxtoNone. Usex.sort()orx = sorted(x).- Accidentally sharing lists via assignment, or with
[[0] * 3] * 3(all rows are the same list). Use[[0] * 3 for _ in range(3)]. KeyErrorwhen reading a missing key withd[key]. Used.get(key)or check within.- Using a list as a dict key or set item raises
TypeError: unhashable type. Convert it to a tuple. - Relying on set order. Sets have no guaranteed order; sort them when order matters.
Exercise
Given a list of orders, compute the total spent per customer and list customers who spent more than 50,000, sorted by name.
orders = [
("Budi", 30000), ("Siti", 45000), ("Budi", 25000),
("Andi", 15000), ("Siti", 20000), ("Dewi", 60000),
]
totals = {}
for customer, amount in orders:
totals[customer] = totals.get(customer, 0) + amount
big_spenders = sorted(name for name, total in totals.items() if total > 50000)
print(totals)
print(big_spenders)
Output:
{'Budi': 55000, 'Siti': 65000, 'Andi': 15000, 'Dewi': 60000}
['Budi', 'Dewi', 'Siti']
Summary
- list: ordered and mutable, for sequences that change.
- tuple: immutable records, great for unpacking and as dict keys.
- dict: fast key-value lookup; use
get(),items(), and|. - set: unique items, fast membership, set algebra.
- Comprehensions build collections concisely; keep them simple.
In Part 6 we will work with strings in depth and learn to read and write files, including CSV and JSON.
Related articles
PostgreSQL Zero to Advanced #5: Aggregation, GROUP BY & HAVING
Turn raw orders into business numbers: total revenue, average order value, sales per month and top customers using COUNT, SUM, AVG, GROUP BY, HAVING and FILTER.
PHP Zero to Advanced #5: Arrays & Array Functions
Master PHP arrays: indexed and associative arrays, nested data, destructuring, and the essential functions array_map, array_filter, array_reduce, usort, and friends.
Ruby on Rails Zero to Advanced #5: Routing, Controllers & Views
Follow a request through Rails: define routes, use resources, read params in controllers, and render dynamic HTML with ERB views and partials.