Programming with Python
Kühne Logistics University Hamburg - Fall 2026
The menu started with two dishes. Kevin 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. Commit. Hands up before the reveal.
The second print shows what?
a) Fresh today: Mate b) Error c) 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
a) 5 — the function works on its own copy. We never caught its result, so the global stock is untouched. To keep a result, assign it back.
a) 6.90 b) 13.80 c) 2
b) 13.80 — total() multiplies qty by price: 2 * 6.90. 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 the classic beginner stumble.
A negative index counts backwards from the end, no need to know the length:
Ayran
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:
['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 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.
['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:
['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:
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):
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.
Open the exercise (scan the QR or type the link):
beyondsimulations.github.io/Introduction-to-Python/notebooks/ex_04_a/
First predict what happens, then run it.
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 dict[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 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:
None
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:
{'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?
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:
KeyError: 'Cola'
A set keeps each value once. Feed it duplicates and they collapse. Perfect for counting distinct things, like today’s regulars:
{'ada', 'nina', 'tom'}
3
Five visits, three people. A set answers “how many different?” in one step.
Open the exercise (scan the QR or type the link):
beyondsimulations.github.io/Introduction-to-Python/notebooks/ex_04_b/
First predict what happens, then run it.
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.”
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 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:
{'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?
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.
[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:
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.
Open the exercise (scan the QR or type the link):
beyondsimulations.github.io/Introduction-to-Python/notebooks/ex_04_c/
First predict what happens, then run it.
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: 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.
Lecture IV - Handling Data in more than one Dimension | Dr. Tobias Vlćek | Home