drinks = ["Mate", "Spezi", "Ayran"]
print(drinks)['Mate', 'Spezi', 'Ayran']
Programming with Python
A list holds many values under one name, in order. That’s the cure for seventeen variables:
drinks = ["Mate", "Spezi", "Ayran"]
print(drinks)['Mate', 'Spezi', 'Ayran']
. . .
Square brackets [], items separated by commas. Add a drink tomorrow and the list just grows. No new variable names.
Each item has an index. Python counts from 0:
drinks = ["Mate", "Spezi", "Ayran"]
print(drinks[0]) # the first item
print(drinks[2]) # the third itemMate
Ayran
. . .
The first item is drinks[0], not drinks[1]. Off-by-one is the classic beginner stumble.
A negative index counts backwards from the end, no need to know the length:
drinks = ["Mate", "Spezi", "Ayran"]
print(drinks[-1]) # last item
print(drinks[-2]) # second to lastAyran
Spezi
. . .
-1 is always the last item. Handy for “the most recent order”.
A slice list[start:stop] returns a new list, from start up to, but not including, stop:
drinks = ["Mate", "Spezi", "Ayran"]
print(drinks[0:2]) # items 0 and 1
print(drinks[-2:]) # the last two — negative start, open end['Mate', 'Spezi']
['Spezi', 'Ayran']
. . .
The stop index is left out. drinks[0:2] gives you two items, not three. And leaving a side empty means “all the way”: drinks[-2:] starts two from the end and runs to the finish.
A courier carries four cup sizes. What does this print?
sizes = ["S", "M", "L", "XL"]
print(sizes[1:3])a) ['M', 'L', 'XL'] b) ['M', 'L'] c) ['S', 'M', 'L']
. . .
Predict first. Commit to an answer before the next slide.
stop index is excludedb) ['M', 'L'] — the slice starts at index 1 ("M") and stops before index 3, so index 3 ("XL") is never included. A slice from 1:3 gives you exactly 3 - 1 = 2 items.
sizes = ["S", "M", "L", "XL"]
print(sizes[1:3])['M', 'L']
Lists are mutable: you can change them after creation. .append() adds one item to the end; + joins two lists into a new one:
drinks = ["Mate", "Spezi"]
drinks.append("Ayran")
print(drinks)
combined = ["Mate"] + ["Kombucha"]
print(combined)['Mate', 'Spezi', 'Ayran']
['Mate', 'Kombucha']
. . .
.append() changes the list in place; + builds a fresh one.
len() tells you how many items a list holds, the honest replacement for counting variables by hand:
drinks = ["Mate", "Spezi", "Ayran"]
print(len(drinks))3
. . .
len() also works on strings (letters) and, soon, on dictionaries (keys).
A tuple looks like a list but uses () and cannot be changed, perfect for a record whose shape never varies, like opening hours (hour, minute):
opening = (9, 0) # 9:00 sharp
print(opening[0])
print(opening[1])9
0
. . .
Indexing and slicing work exactly as on lists. You just can’t .append() to a tuple. Heads up: some tools quietly turn a tuple into a list when they store it. Remember that in Part II.
A list finds items by position. But “what does a Spezi cost?” is a question about a name, not a position. A dictionary maps a key to a value:
prices = {"Mate": 3.50, "Spezi": 3.20, "Ayran": 2.80}
print(prices["Spezi"])3.2
. . .
Curly braces {}, each pair written key: value. No more remembering that Spezi lives at index 1.
Keys are usually strings; values can be anything. Read a value with dict[key]:
prices = {"Mate": 3.50, "Spezi": 3.20, "Ayran": 2.80}
print(prices["Mate"])
print(prices["Ayran"])3.5
2.8
. . .
The same square brackets as a list, but now the thing inside is a key, not a number.
Ask for a key that doesn’t exist and Python raises a KeyError. It stops rather than guess:
prices = {"Mate": 3.50, "Spezi": 3.20}
print(prices["Cola"])
# KeyError: 'Cola'. . .
A KeyError is not a crash to fear. It’s Python telling you the key is spelled wrong or was never added. You’ll read exactly this error in tonight’s lab.
.get().get() returns None for a missing key instead of raising, and you can supply a fallback:
prices = {"Mate": 3.50, "Spezi": 3.20}
print(prices.get("Cola")) # missing → None
print(prices.get("Cola", 0)) # missing → your fallbackNone
0
. . .
Use [] when the key must be there; use .get() when it might not.
Assigning to a key updates it if it exists, or adds it if it doesn’t. Work on a copy (dict()) when the original must survive:
prices = {"Mate": 3.50, "Spezi": 3.20}
winter = dict(prices) # a copy — keep the original safe
winter["Spezi"] = 3.40 # update an existing key
winter["Kombucha"] = 4.20 # add a brand-new key
print(winter)
print(prices) # untouched — the summer menu comes back in April{'Mate': 3.5, 'Spezi': 3.4, 'Kombucha': 4.2}
{'Mate': 3.5, 'Spezi': 3.2}
. . .
One syntax, two jobs. Python decides by whether the key is already present. And the copy means next April needs no un-editing.
A customer orders a Cola. It’s not on the menu. What does this line do?
prices = {"Mate": 3.50, "Spezi": 3.20}
print(prices["Cola"])a) prints None b) prints "" c) raises KeyError d) adds "Cola"
. . .
Predict first. Commit to an answer before the next slide.
[] demands the keyc) raises KeyError — square-bracket reads never invent a value and never add one. Reach for .get() when a key might be missing:
prices = {"Mate": 3.50, "Spezi": 3.20}
try:
print(prices["Cola"])
except KeyError as missing:
print("KeyError:", missing)KeyError: 'Cola'
A set keeps each value once. Feed it duplicates and they collapse. Perfect for counting distinct things, like today’s regulars:
visitors = ["nina", "tom", "nina", "ada", "tom"]
regulars = set(visitors)
print(regulars)
print(len(regulars)) # how many different people{'ada', 'nina', 'tom'}
3
. . .
Five visits, three people. A set answers “how many different?” in one step.
Values can themselves be dictionaries. Each delivery zone carries its own fee and estimated time:
zones = {
"Altstadt": {"fee": 1.50, "eta": 12},
"Hafen": {"fee": 2.50, "eta": 20},
}
print(zones["Hafen"]){'fee': 2.5, 'eta': 20}
. . .
One key ("Hafen") points to a whole dictionary. This is how real data grows: structures inside structures.
To reach an inner value, use two keys in a row, outer first, then inner:
zones = {
"Altstadt": {"fee": 1.50, "eta": 12},
"Hafen": {"fee": 2.50, "eta": 20},
}
print(zones["Hafen"]["fee"])
print(zones["Altstadt"]["eta"])2.5
12
. . .
Read it left to right: “in zones, take Hafen, then its fee.”
You already know the loop that builds a list. A list comprehension is that same loop, written on one line:
counts = [2, 1, 3]
# the loop you know
doubled = []
for c in counts:
doubled.append(c * 2)
print(doubled)
# the one-liner
doubled = [c * 2 for c in counts]
print(doubled)[4, 2, 6]
[4, 2, 6]
. . .
Read it as “c * 2 for each c in counts”. Same result, less typing.
round inside a comprehensionAny expression can go in front of the for, including round(). Loyalty pricing: 10% off, cleaned to two decimals:
drink_prices = [3.50, 3.20, 2.80]
print([p * 0.9 for p in drink_prices]) # raw floats — ugly
print([round(p * 0.9, 2) for p in drink_prices]) # rounded — clean[3.15, 2.8800000000000003, 2.52]
[3.15, 2.88, 2.52]
. . .
The raw version leaks a 2.88000...3 float. Wrapping each price in round(_, 2) is the same receipt trick you use for money.
Swap the brackets for braces and give a key: value. Now you rebuild a whole menu in one line:
prices = {"Mate": 3.50, "Spezi": 3.20, "Ayran": 2.80}
loyalty = {name: round(price * 0.9, 2) for name, price in prices.items()}
print(loyalty){'Mate': 3.15, 'Spezi': 2.88, 'Ayran': 2.52}
. . .
.items() hands you each key, value pair to work with. The whole discounted menu, no loop body in sight.
What does this build?
nums = [1, 2, 3, 4]
print([n * n for n in nums])a) [1, 4, 9, 16] b) [2, 4, 6, 8] c) [1, 2, 3, 4]
. . .
Predict first. Commit to an answer before the next slide.
a) [1, 4, 9, 16] — the expression n * n runs once per item, so each number becomes its own square. 2 * n would double them; the bare n would just copy the list.
nums = [1, 2, 3, 4]
print([n * n for n in nums])[1, 4, 9, 16]
Your browser notebook has no files on it. So in Part I, data ships inside the code, as lists, dicts, or a multi-line string you can split apart:
orders = """Mate;2
Spezi;1
Ayran;3"""
for line in orders.splitlines():
name, qty = line.split(";")
print(name, qty)Mate 2
Spezi 1
Ayran 3
. . .
Reading real files from your own machine comes in Part III, and pandas loads them properly in Session VIII. For now, inline data is all you need.
. . .
Download your .py before you leave. Closing the tab without downloading loses your work, and downloading is exactly how you hand in the checkpoints.
0, count back with -1, slice with a stop that’s excludedKeyError, so reach for .get() when it might not be there; sets keep each value once[expr for x in things] for lists, {k: v for ...} for dicts, round() welcome inside. . .
Next time — Episode 5: Kevin rewrites the checkout at 3 AM on four energy drinks. What could go wrong? We learn to read tracebacks and catch errors before the customer does.
. . .
Nothing new here, but these are still great books to start with!
. . .
For more, see the literature list of this course.