Programming with Python
Kühne Logistics University Hamburg - Fall 2026
The menu started with two dishes. Tobi tracked them in variables: price1, price2. Then came price3, a price_final, and, after a rough Tuesday, a price_final_FINAL2.
Seventeen variables for one menu. Add a dish and you touch seventeen lines. Today the data gets structure: one name that holds many values, and a way to look things up by name instead of by counting.
Three questions from Episode 3.
The second print shows what?
a) Fresh today: Mate b) Error c) None
Fresh today: Mate
None
c) None: announce has no return, so it hands back None. Printing something is not the same as returning it.
a) 5 b) 15 c) Error
5
a) 5. n = n + 10 points the function’s own name n at a new number, so the global stock never moves. We never caught the result. To keep a result, assign it back.
a) 6.9 b) 13.8 c) 2
13.8
b) 13.8. total() multiplies qty by price: 2 * 6.90, and print shows a float without the trailing zero. The object carries its own data, the method does the arithmetic.
A list holds many values under one name, in order. That’s the cure for seventeen variables:
['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:
Mate
Ayran
The first item is drinks[0], not drinks[1]. Off-by-one is a classic mistake of students.
A negative index counts backwards from the end, no need to know the length:
Ayran
Spezi
-1 is always the last item. Useful for getting “the most recent order”.
A slice list[start:stop] returns a new list, from start up to, but not including, stop:
['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?
a) ['M', 'L', 'XL'] b) ['M', 'L'] c) ['S', 'M', 'L']
Predict. Pick a letter, then I reveal the answer.
stop index is excluded['M', 'L']
b) ['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.
Scan the QR or type the link:
python.tobiasvlcek.com/notebooks/ex_04_a/
Any questions? Let me know!
Lists are mutable: you can change them after creation. .append() adds one item to the end; + joins two lists into a new one:
['Mate', 'Spezi', 'Ayran']
['Mate', 'Kombucha']
.append() changes the list itself, and every name pointing at it sees that; + builds a fresh one. = never changes a list: it only points a name at one.
Tobi wants a winter copy of the drinks list to experiment on. What does the last line print?
a) ['Mate', 'Spezi'] b) ['Ayran', 'Mate', 'Spezi'] c) ['Mate', 'Spezi', 'Ayran']
Predict. Pick a letter, then I reveal the answer.
['Mate', 'Spezi', 'Ayran']
c) ['Mate', 'Spezi', 'Ayran']. winter = summer does not copy anything: both names point at the same list, so .append() through one name shows up under the other. A real copy is list(summer) or summer[:] (a slice with both ends open, so all of it).
len() tells you how many items a list holds:
3
len() also works on strings (letters) and, soon, on dictionaries (keys).
A loop over the list hands you each item. Sometimes you need to know where an item sits. Then loop over the positions instead:
0 Mate
1 Spezi
2 Ayran
range(len(drinks)) counts 0, 1, 2: one number per position, and drinks[i] is the item that sits there.
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):
9
0
9 0
Indexing and slicing work exactly as on lists. You just can’t .append() to a tuple.
Scan the QR or type the link:
python.tobiasvlcek.com/notebooks/ex_04_b/
Any questions? Let me know!
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:
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 square brackets and its key:
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:
A KeyError means the key is spelled wrong or was never added. However, there is a smarter way to ask for a key.
.get().get() returns None for a missing key instead of raising, and you can supply a fallback:
None
0
Use [] when the key must be there; use .get() when it might not.
in asks whether a value is in a list: "Mate" in drinks is True. On a dictionary, what does it look at?
a) True False b) True True c) False True
Predict. Pick a letter, then I reveal the answer.
in checks the keysTrue False
True
a) True False. Membership on a dictionary looks at the keys only. 3.20 is a value, so Python says it is not there. To search the values, ask 3.20 in prices.values().
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:
{'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.
A customer orders a Cola. It’s not on the menu. What does this line do?
a) prints None b) prints "" c) raises KeyError d) adds "Cola"
Predict. Pick a letter, then I reveal the answer.
[]KeyError: 'Cola'
c) raises KeyError: square-bracket reads never invent a value and never add one. Reach for .get() when a key might be missing.
A set keeps each value once. Feed it duplicates and they collapse. Perfect for counting distinct things, like today’s regulars:
{'tom', 'ada', 'nina'}
3
Five visits, three people. A set answers “how many different?”.
Scan the QR or type the link:
python.tobiasvlcek.com/notebooks/ex_04_c/
Any questions? Let me know!
Values can themselves be dictionaries. Each delivery zone carries its own fee and estimated time:
{'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:
2.5
12
Read it left to right: “in zones, take Hafen, then its fee.”
Scan the QR or type the link:
python.tobiasvlcek.com/notebooks/ex_04_d/
Any questions? Let me know!
You already know the loop that builds a list. A list comprehension is that same loop, written on one line:
[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:
[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 thing we already used for money.
.items() hands you the menu as (key, value) pairs. Give the for two names and each pair is unpacked into them, like hour, minute = opening:
Mate 3.5
Spezi 3.2
Ayran 2.8
One name per slot, left to right. Two names, two slots.
Swap the brackets for braces and give a key: value. Now you rebuild a whole menu in one line:
{'Mate': 3.15, 'Spezi': 2.88, 'Ayran': 2.52}
The same name, price loop, now inside braces. The whole discounted menu, no loop body in sight.
What does this build?
a) [1, 4, 9, 16] b) [2, 4, 6, 8] c) [1, 2, 3, 4]
Predict. Pick a letter, then I reveal the answer.
[1, 4, 9, 16]
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.
Some dishes sold nothing today. A comprehension can carry an if after the for. How long is the result?
a) [4, 0, 6, 0, 2] b) [4, 6, 2] c) [2, 3, 1]
Predict. Pick a letter, then I reveal the answer.
if drops items[4, 6, 2]
b) [4, 6, 2]. The if is a filter: items that fail it never reach the expression, so the result is shorter than the input. c * 2 still runs on the survivors, which rules out [2, 3, 1].
Scan the QR or type the link:
python.tobiasvlcek.com/notebooks/ex_04_e/
Any questions? Let me know!
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 insideNext time, Episode 5 starts with Checkpoint 2: 40 minutes, no AI, everything from Sessions III-IV. Then Tobi 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.
Lecture IV - Handling Data in more than one Dimension | Dr. Tobias Vlćek | Home