Home

Appendix 0 Part 2: Introduction to Programming with Python

appendix python programming oop functions classes beginners

New to the terminal? Read Appendix 0 Part 1: Terminal and CLI Foundations first. This appendix assumes you can open a terminal and run commands.

This is the second part of the pre-course material. Part 1 taught you how to talk to your computer through a terminal. Now we answer a bigger question: why would you want to?

By the end of this appendix, you will:

This is pre-course reading. Work through it at your own pace. You do not need to memorize syntax – the goal is to build intuition for how programmers think.


1. Why Do People Write Programs?

Every piece of software you have ever used exists because someone had a problem and decided to solve it with code.

Think about the applications on your phone right now. Instagram exists because Kevin Systrom wanted a simple way to share photos with filters. Spotify exists because Daniel Ek wanted to make music accessible without piracy. Google exists because Larry Page and Sergey Brin wanted to find relevant information in a growing ocean of web pages. Netflix started mailing DVDs but then wrote software that could recommend movies better than any human clerk.

These are all problems that were solved with programs.

But software is not just consumer apps. Programming is everywhere:

The common thread? Someone had a problem. Code was the tool to solve it.

1.1 From Big Ideas to Small Steps

Of course, you cannot sit down and build Netflix on your first day. The engineers at Netflix did not start there either – they started by learning the same fundamentals you are about to learn.

The trick to learning programming is finding problems that are small enough to solve today but meaningful enough to care about. Writing Hello World on the screen teaches you almost nothing, because it solves no real problem. But converting temperatures, analyzing data, or computing statistics – those are real tasks that use real thinking.

Throughout this appendix, we will work with a single running example: a weather station. Everyone understands weather. It involves numbers (temperatures, humidity, wind speed), text (city names, descriptions), and logical decisions (is it raining?). It is simple enough to start with, but rich enough to show you why each new programming concept matters.

Let us begin.


2. Why Python?

There are hundreds of programming languages. Why does this course use Python?

Three reasons:

1. No compilation step. In many languages (C, Java, Rust), you write code, then run a separate program called a compiler to translate it into something the computer can execute, and only then can you run it. Python skips the middle step. You write code and run it immediately. This makes experimenting fast and painless.

Language Step 1 Step 2 Step 3
C Write main.c Compile: gcc main.c -o main Run: ./main
Java Write Main.java Compile: javac Main.java Run: java Main
Python Write main.py Run: python main.py

2. Designed for fast prototyping. Guido van Rossum created Python in 1991 with a philosophy: code should be easy to read, easy to write, and quick to turn an idea into a working program. Python was built for humans, not for machines.

3. The language of science and AI. Today, Python is the dominant language in data science, machine learning, scientific computing, and automation. Libraries like NumPy, Pandas, TensorFlow, and PyTorch are all Python-first. If you work with data or AI in any field, you will encounter Python.

Here is your first Python program. Open a terminal (you learned how in Part 1), create a file called hello.py, and run it:

# save this as hello.py
print("Hello from Python!")
$ python hello.py
Hello from Python!

That is it. No compiler. No build step. Write, run, see the result.


3. Elementary Data Types and Calculations

Any problem you want to solve with code requires some form of calculation. Programming languages give you basic building blocks to represent and manipulate data – numbers, text, and logical values. If you have done any math at all, you already understand these concepts.

3.1 Numbers and Strings: The Building Blocks

In math, you work with integers (1, 2, 3) and real numbers (3.14, 2.718). In Python, these are called int and float:

temperature_celsius = 23.5      # float (decimal number)
number_of_sensors = 4            # int (whole number)
city = "Aalen"                   # str (text, called a "string")
is_raining = False               # bool (True or False)

A variable is simply a name attached to a value – think of it as a labeled box. You choose the name, Python remembers the value.

Notice print() – you have been using it since your very first hello.py and might not have thought twice about it. print() is a built-in function that Python gives you for free. You pass it a value, and it displays that value on the screen. You do not need to understand how it works inside – just that it takes something in and makes it visible. In Section 4, you will learn to write your own functions. When you get there, you will realize you have already been calling one since your first line of code.

You can also ask Python what type a value is by combining print() with another built-in function, type():

print(type(temperature_celsius))   # <class 'float'>
print(type(number_of_sensors))     # <class 'int'>
print(type(city))                  # <class 'str'>
print(type(is_raining))            # <class 'bool'>

That is all you need to know about types for now. There are four basic ones:

Type What It Represents Examples
int Whole numbers 0, 42, -7
float Decimal numbers 3.14, -0.5, 23.5
str Text (strings of characters) "Aalen", "hello", "42"
bool Logical values True, False

3.2 Operators: Doing Math with Code

You know arithmetic. Python does too:

celsius = 23.5

# The classic conversion formula
fahrenheit = celsius * 9 / 5 + 32

print(fahrenheit)   # 74.3

The arithmetic operators work exactly like you expect:

a = 10
b = 3

print(a + b)    # 13    addition
print(a - b)    # 7     subtraction
print(a * b)    # 30    multiplication
print(a / b)    # 3.33  division (always returns float)
print(a // b)   # 3     integer division (rounds down)
print(a % b)    # 1     modulo (remainder)
print(a ** b)   # 1000  exponentiation (10 to the power of 3)

You can also compare values – the result is always a bool:

temperature = 23.5

print(temperature > 30)     # False
print(temperature < 0)      # False
print(temperature == 23.5)  # True
print(temperature >= 20)    # True

With just variables and operators, you can already do useful work. Let us convert a temperature:

# Weather station in Aalen reads 23.5 degrees Celsius
celsius_aalen = 23.5
fahrenheit_aalen = celsius_aalen * 9 / 5 + 32
print("Aalen:", fahrenheit_aalen, "F")   # Aalen: 74.3 F

3.3 Collections: Lists

In the real world, you rarely deal with a single value. A weather station records temperatures every hour. You need a way to store multiple values together.

A list is an ordered sequence of values:

# Daily temperatures for one week in Aalen (Celsius)
daily_temps = [18.2, 21.0, 19.5, 23.5, 25.1, 22.8, 20.3]

print(daily_temps[0])       # 18.2  (first element, counting starts at 0)
print(daily_temps[3])       # 23.5  (fourth element)
print(len(daily_temps))     # 7     (number of elements)

You can loop through a list to process every element:

# Compute the average temperature
total = 0
for temp in daily_temps:
    total = total + temp

average = total / len(daily_temps)
print("Average:", average)   # Average: 21.485714285714288

Lists are powerful, but they are just containers. As your code grows, you will need more structure. Let us see why.

3.4 The Problem: Repetition

Suppose you monitor five cities. You need to convert each temperature to Fahrenheit:

# Five cities, five conversions
celsius_aalen = 23.5
fahrenheit_aalen = celsius_aalen * 9 / 5 + 32

celsius_berlin = 19.0
fahrenheit_berlin = celsius_berlin * 9 / 5 + 32

celsius_munich = 21.3
fahrenheit_munich = celsius_munich * 9 / 5 + 32

celsius_stuttgart = 20.7
fahrenheit_stuttgart = celsius_stuttgart * 9 / 5 + 32

celsius_hamburg = 16.8
fahrenheit_hamburg = celsius_hamburg * 9 / 5 + 32

This works. But look at the repetition. The same formula appears five times. Now imagine:

Copy-pasting code is a recipe for bugs. There must be a better way.

Try this now: Open the Guided Hands-On Notebook (Sections 1-2) and:

  1. Create a variable celsius with today’s approximate temperature
  2. Convert it to Fahrenheit using the formula: fahrenheit = celsius * 9 / 5 + 32
  3. Create a list of five temperatures and compute their average using a loop

4. Functions: Reuse Instead of Repetition

You already know functions from math class. If your teacher wrote:

\(f(x) = x \cdot \frac{9}{5} + 32\)

you would immediately understand: give me any value of \(x\), and I will return the result of that formula. You do not rewrite the formula every time – you just call \(f(23.5)\) and get 74.3.

Python functions work the same way.

4.1 From Math to Code

Here is the temperature conversion as a Python function:

def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

That is it. def means “define a function.” The name is celsius_to_fahrenheit. It takes one input (celsius) and returns one output. Now the five-city problem becomes:

def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

print("Aalen:", celsius_to_fahrenheit(23.5))       # 74.3
print("Berlin:", celsius_to_fahrenheit(19.0))       # 66.2
print("Munich:", celsius_to_fahrenheit(21.3))       # 70.34
print("Stuttgart:", celsius_to_fahrenheit(20.7))    # 69.26
print("Hamburg:", celsius_to_fahrenheit(16.8))       # 62.24

The formula exists once. If you need to change it, you change it in one place. If you have 100 cities, you just call the function 100 times.

Even better – combine it with a list:

cities = [
    ["Aalen", 23.5],
    ["Berlin", 19.0],
    ["Munich", 21.3],
    ["Stuttgart", 20.7],
    ["Hamburg", 16.8],
]

for city_data in cities:
    print(city_data[0] + ":", celsius_to_fahrenheit(city_data[1]))

Five cities or five thousand – the code stays the same length.

4.2 Functions with Multiple Inputs and Outputs

Functions can take multiple parameters and return multiple values:

def analyze_temperatures(readings):
    """Analyze a list of temperature readings."""
    min_val = min(readings)
    max_val = max(readings)
    avg_val = sum(readings) / len(readings)
    return min_val, max_val, avg_val

daily_temps = [18.2, 21.0, 19.5, 23.5, 25.1, 22.8, 20.3]

lowest, highest, average = analyze_temperatures(daily_temps)
print("Lowest:", lowest)     # 18.2
print("Highest:", highest)   # 25.1
print("Average:", average)   # 21.49

Functions are a huge step forward. They eliminate repetition and make your code readable. But as problems grow, functions alone are not enough.

4.3 The Problem: Too Many Loose Values

Your weather station records more than just temperature. It also tracks humidity, wind speed, the city name, and a timestamp. Now your function needs all of those:

def report_weather(city, temperature, humidity, wind_speed, timestamp):
    fahrenheit = celsius_to_fahrenheit(temperature)
    print(city, "at", timestamp)
    print("  Temperature:", temperature, "C /", fahrenheit, "F")
    print("  Humidity:", humidity, "%")
    print("  Wind:", wind_speed, "km/h")

def feels_like(temperature, humidity, wind_speed):
    # simplified wind chill calculation
    return temperature - (wind_speed * 0.7)

# Calling these functions requires passing the same values over and over
report_weather("Aalen", 23.5, 65.0, 12.3, "2026-03-11 14:00")
wind_chill = feels_like(23.5, 65.0, 12.3)

Notice the problem? The values 23.5, 65.0, 12.3 belong together – they are all part of the same weather reading. But we pass them around as separate, loose values. What if you accidentally swap humidity and wind speed? Python will not complain. Your results will just be wrong.

When you find yourself passing the same group of values to multiple functions, those values are asking to be grouped together.

Passing loose values
temperature
humidity
wind_speed
city
timestamp
▼▼▼▼▼
report_weather(city, temperature, humidity, wind_speed, timestamp)
▶ much cleaner
Passing one object
WeatherReading
temperature humidity wind_speed city timestamp
▼
report_weather(reading)

Try this now (Notebook Section 3):

  1. Write a function celsius_to_fahrenheit(celsius) that converts Celsius to Fahrenheit
  2. Write a function feels_like(temp, humidity, wind) that returns temp - (wind * 0.7)
  3. Call both functions for three different cities
  4. Notice how many arguments you repeat each time

Think of a class as a blank form – a template with labeled fields. A “Weather Reading Form” has fields for temperature, humidity, wind speed, and city name. Every time you fill out one of those forms, you create an instance – one specific reading with concrete values.

In Python, you define the form (the class) once, then create as many filled-out copies (instances) as you need.

5.1 Your First Class

class WeatherReading:
    def __init__(self, city, temperature, humidity, wind_speed):
        self.city = city
        self.temperature = temperature
        self.humidity = humidity
        self.wind_speed = wind_speed

Let us unpack this:

Now create instances:

aalen = WeatherReading("Aalen", 23.5, 65.0, 12.3)
berlin = WeatherReading("Berlin", 19.0, 72.0, 8.5)
munich = WeatherReading("Munich", 21.3, 58.0, 15.1)

# Access individual fields with dot notation
print(aalen.city)          # Aalen
print(aalen.temperature)   # 23.5
print(berlin.humidity)     # 72.0

The data that belongs together now lives together in one object.

5.2 Classes as Function Parameters

Remember the messy function with five parameters? With a class, it becomes clean:

# Before: five separate parameters
def report_weather(city, temperature, humidity, wind_speed, timestamp):
    ...

# After: one object that carries everything
def report_weather(reading):
    fahrenheit = celsius_to_fahrenheit(reading.temperature)
    print(reading.city)
    print("  Temperature:", reading.temperature, "C /", fahrenheit, "F")
    print("  Humidity:", reading.humidity, "%")
    print("  Wind:", reading.wind_speed, "km/h")

report_weather(aalen)   # clean, readable, no chance of swapping values

One parameter instead of five. No risk of swapping humidity and wind speed. And if you later add a pressure field to WeatherReading, you do not need to change the function signature at all.

5.3 The Problem: Functions That Live Separately from Their Data

We now have a WeatherReading class and standalone functions like celsius_to_fahrenheit and feels_like. But celsius_to_fahrenheit only makes sense in the context of a weather reading. And feels_like uses temperature, humidity, and wind_speed – which are all inside the WeatherReading object.

Why keep these functions separate from the data they operate on? Would it not make more sense to put them inside the class?

Try this now (Notebook Section 4):

  1. Define a WeatherReading class with city, temperature, humidity, and wind_speed
  2. Create three instances for three different cities
  3. Store them in a list and loop through them:
    stations = [aalen, berlin, munich]
    for station in stations:
        print(station.city, station.temperature)
    

6. Methods: Functions Inside Classes

A method is simply a function that lives inside a class and operates on that class’s data. Instead of passing data to a function from the outside, the method already has access to the data through self.

6.1 Adding Behavior to Data

Let us move our conversion and feels-like calculations inside the class:

class WeatherReading:
    def __init__(self, city, temperature, humidity, wind_speed):
        self.city = city
        self.temperature = temperature
        self.humidity = humidity
        self.wind_speed = wind_speed

    def to_fahrenheit(self):
        return self.temperature * 9 / 5 + 32

    def feels_like(self):
        return self.temperature - (self.wind_speed * 0.7)

Compare the before and after:

# Before (standalone function): you pass the data in
feels_like(23.5, 65.0, 12.3)

# After (method): the object already has the data
aalen = WeatherReading("Aalen", 23.5, 65.0, 12.3)
aalen.feels_like()

The method version is cleaner: the object knows its own data and can perform calculations on it. You do not need to remember which values to pass – they are already inside the object.

Now you can process a list of stations elegantly:

stations = [
    WeatherReading("Aalen", 23.5, 65.0, 12.3),
    WeatherReading("Berlin", 19.0, 72.0, 8.5),
    WeatherReading("Munich", 21.3, 58.0, 15.1),
]

for station in stations:
    print(station.city)
    print("  Fahrenheit:", station.to_fahrenheit())
    print("  Feels like:", station.feels_like())

6.2 The __str__ Method: Making Objects Printable

If you try print(aalen) right now, you get something ugly:

<__main__.WeatherReading object at 0x7f8b2c3d4e50>

That is Python telling you “this is a WeatherReading object stored at memory address such-and-such.” Not very helpful. You can fix this by defining a special method called __str__:

class WeatherReading:
    def __init__(self, city, temperature, humidity, wind_speed):
        self.city = city
        self.temperature = temperature
        self.humidity = humidity
        self.wind_speed = wind_speed

    def to_fahrenheit(self):
        return self.temperature * 9 / 5 + 32

    def feels_like(self):
        return self.temperature - (self.wind_speed * 0.7)

    def __str__(self):
        return (self.city + ": " + str(self.temperature) + " C, "
                + str(self.humidity) + "% humidity, "
                + str(self.wind_speed) + " km/h wind")

Now:

aalen = WeatherReading("Aalen", 23.5, 65.0, 12.3)
print(aalen)
# Aalen: 23.5 C, 65.0% humidity, 12.3 km/h wind

Methods whose names start and end with double underscores (like __init__ and __str__) are called dunder methods (short for “double underscore”). They let you customize how Python handles your objects. You will encounter more of them as you progress.

6.3 The Problem: A New Kind of Station

Your city installs advanced weather stations that also measure UV index and air pressure. You need a new class for these readings, but it should still do everything WeatherReading already does – convert to Fahrenheit, calculate feels-like temperature, print nicely – plus handle the new measurements.

Do you copy-paste the entire WeatherReading class and add two fields? That would work, but you would have two copies of the same code. If you fix a bug in one, you must remember to fix it in the other.

WeatherReading
+ str city
+ float temperature
+ float humidity
+ float wind_speed
+ to_fahrenheit()
+ feels_like()
+ __str__()

There must be a way to say: “I want a new class that already does everything this old class can do, plus more.”

Try this now (Notebook Section 5):

  1. Add a method is_comfortable(self) to your WeatherReading that returns True if temperature is between 18 and 26 and humidity is below 70
  2. Add a method summary(self) that returns a formatted string like "Aalen: 23.5C, feels like 14.9C"
  3. Create three instances and call both methods on each

7. Inheritance and Polymorphism: Extending What Already Works

“I want a new class that already does everything the old one could.”

That sentence is the entire motivation behind inheritance. You create a new class that inherits all the attributes and methods of an existing class, then add or change only what is different.

7.1 Building on Existing Classes

class AdvancedReading(WeatherReading):
    def __init__(self, city, temperature, humidity, wind_speed, uv_index, pressure):
        super().__init__(city, temperature, humidity, wind_speed)
        self.uv_index = uv_index
        self.pressure = pressure

Let us unpack this:

Now the advanced reading can do everything the basic one can:

reading = AdvancedReading("Aalen", 23.5, 65.0, 12.3, 6.2, 1013.25)

# Inherited methods -- these just work!
print(reading.to_fahrenheit())   # 74.3
print(reading.feels_like())      # 14.89
print(reading.city)              # Aalen

# New data
print(reading.uv_index)         # 6.2
print(reading.pressure)         # 1013.25

You wrote zero conversion or feels-like code for AdvancedReading. It inherited everything from WeatherReading for free.

7.2 Overriding Methods

What if the advanced station’s feels_like() calculation should also account for UV index? You can override the parent’s method by defining a new version in the child class:

class AdvancedReading(WeatherReading):
    def __init__(self, city, temperature, humidity, wind_speed, uv_index, pressure):
        super().__init__(city, temperature, humidity, wind_speed)
        self.uv_index = uv_index
        self.pressure = pressure

    def feels_like(self):
        # Start with the basic wind chill, then add UV impact
        base = self.temperature - (self.wind_speed * 0.7)
        uv_adjustment = self.uv_index * 0.5
        return base + uv_adjustment

    def __str__(self):
        base = super().__str__()
        return base + ", UV " + str(self.uv_index) + ", " + str(self.pressure) + " hPa"

Now AdvancedReading has its own feels_like() that considers UV, and its own __str__() that includes the extra fields. But to_fahrenheit() is still inherited unchanged from WeatherReading – no need to rewrite what already works.

7.3 One Interface, Many Types

Here is where things get powerful. You can mix basic and advanced readings in the same list and treat them uniformly:

stations = [
    WeatherReading("Aalen", 23.5, 65.0, 12.3),
    AdvancedReading("Berlin", 19.0, 72.0, 8.5, 3.1, 1015.0),
    WeatherReading("Munich", 21.3, 58.0, 15.1),
    AdvancedReading("Stuttgart", 20.7, 60.0, 10.2, 5.8, 1012.5),
]

for station in stations:
    print(station.city, "- feels like:", station.feels_like())

Python calls the right version of feels_like() automatically. For WeatherReading objects, it uses the basic wind chill. For AdvancedReading objects, it uses the UV-aware version. Same method name, different behavior depending on the object’s type.

This is called polymorphism – from Greek: “many forms.” One interface, many implementations. And it is one of the most powerful ideas in programming.

WeatherReading
+ str city
+ float temperature
+ float humidity
+ float wind_speed
+ to_fahrenheit()
+ feels_like()
+ __str__()
▲
inherits from
AdvancedReading
+ float uv_index
+ float pressure
+ feels_like() override
+ __str__() override

And that is genuinely all you need to know about object-oriented programming for this course. Variables, functions, classes, methods, inheritance, polymorphism – you have the complete mental map.

Try this now (Notebook Section 6):

  1. Create an AdvancedReading class that inherits from WeatherReading
  2. Add uv_index and pressure fields
  3. Override __str__ to include the new fields
  4. Create a mixed list of both types and loop through it calling .feels_like()

8. Libraries and Imports: Standing on the Shoulders of Giants

You now know how to structure your own code. But here is a secret every experienced programmer knows: most of the code you need has already been written by someone else.

Python’s greatest strength is not just the language itself – it is the enormous ecosystem of libraries (also called packages or modules) that you can use for free. Why write your own statistics functions when Python includes one?

8.1 Built-in Libraries

Python comes with a “standard library” – a collection of modules that are always available, no installation needed:

import statistics

daily_temps = [18.2, 21.0, 19.5, 23.5, 25.1, 22.8, 20.3]

print(statistics.mean(daily_temps))     # 21.485714285714285
print(statistics.median(daily_temps))   # 21.0
print(statistics.stdev(daily_temps))    # 2.3380903889000912

Remember the loop we wrote earlier to compute the average? statistics.mean() does the same thing in one line – and it handles edge cases (like empty lists) that our loop did not.

Other useful built-in modules:

import math
print(math.sqrt(144))       # 12.0
print(math.pi)              # 3.141592653589793

from datetime import datetime
now = datetime.now()
print(now)                   # 2026-03-11 14:30:00.123456

import random
print(random.randint(1, 6))  # a random dice roll

The import statement loads a module. from X import Y loads a specific piece from a module. That is all the syntax you need.

8.2 Third-Party Libraries

Beyond the standard library, there are hundreds of thousands of community-built packages. You install them with pip (or uv, which this course uses):

$ pip install numpy pandas matplotlib

You will encounter these throughout the course:

We will not dive deeper into these now. The important insight is this: programming is not about writing everything from scratch. It is about combining your code with existing tools to solve problems efficiently.

For a detailed guide on how Python modules and imports work, see Appendix 2: Python Modules.

8.3 Organizing Your Own Code into Modules

When your file gets long, you can split your code into multiple files. Put WeatherReading in its own file and import it elsewhere:

# In weather.py
class WeatherReading:
    def __init__(self, city, temperature, humidity, wind_speed):
        self.city = city
        self.temperature = temperature
        self.humidity = humidity
        self.wind_speed = wind_speed

    def to_fahrenheit(self):
        return self.temperature * 9 / 5 + 32
# In main.py
from weather import WeatherReading

aalen = WeatherReading("Aalen", 23.5, 65.0, 12.3)
print(aalen.to_fahrenheit())

This is the same idea as using someone else’s library – except the library is your own code. The mindset is always the same: define once, reuse everywhere.

Try this now (Notebook Section 7):

  1. Use import statistics to compute the mean and standard deviation of your temperature list
  2. Use from datetime import datetime to print the current date and time
  3. Try import this in a Python terminal and read “The Zen of Python” – it is the philosophy behind Python’s design

9. The Big Picture: How It All Connects

Let us step back and see the path we traveled. Every concept was introduced because the previous one hit a wall:

➕
Raw calculations
variables + operators
Too much repetition
▼
𝑓
Functions
reusable blocks of logic
Too many loose parameters
▼
□
Classes
group related data
Functions separate from data
▼
▣
Methods
behavior inside classes
Want to extend without rewriting
▼
◇
Inheritance & Polymorphism
build on what exists
Don't reinvent the wheel
▼
📦
Libraries & Imports
reuse others' work
Concept Problem It Solves Python Syntax Weather Example
Variables & Operators Store and calculate values x = 23.5 Convert one temperature
Functions Eliminate repetition def f(x): return ... Convert any temperature
Classes Group related data class X: ... Bundle city + temp + humidity
Methods Attach behavior to data def f(self): ... reading.to_fahrenheit()
Inheritance Extend without rewriting class Y(X): ... AdvancedReading adds UV
Polymorphism One interface, many types Same method, different behavior Mixed station list
Imports Reuse existing solutions import X statistics.mean(temps)

This progression is not arbitrary. It reflects how the field of programming itself evolved over decades. Early programs were just sequences of calculations. Functions were invented to manage complexity. Classes and objects emerged when programs grew large enough that grouping data with behavior became essential. Libraries arose because no one wants to solve the same problem twice.

You just walked the same path – in one afternoon.


10. What You Need for This Course

Take a breath. You have covered a lot of ground, and you do not need to memorize everything right now. This appendix gave you the conceptual map – the why behind each idea. The syntax will become second nature as you practice.

Here is what matters for the weeks ahead:

The most important thing to take away: programming is structured problem-solving. Every concept you learned today exists because it helps you solve problems more effectively. Keep that mindset, and the syntax will follow.


11. Quick Reference: Python Concepts at a Glance

A compact cheat sheet for the concepts covered in this appendix. Bookmark this page and come back whenever you need a reminder.

Variable assignment:

name = "Aalen"
temperature = 23.5

List:

temps = [18.2, 21.0, 19.5]

Function:

def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

Class with __init__:

class WeatherReading:
    def __init__(self, city, temperature):
        self.city = city
        self.temperature = temperature

Method:

class WeatherReading:
    def to_fahrenheit(self):
        return self.temperature * 9 / 5 + 32

Inheritance:

class AdvancedReading(WeatherReading):
    def __init__(self, city, temperature, uv_index):
        super().__init__(city, temperature)
        self.uv_index = uv_index

Import:

import statistics
from datetime import datetime
from weather import WeatherReading

Loop:

for station in stations:
    print(station.to_fahrenheit())

Conditional:

if temperature > 30:
    print("Hot!")
elif temperature > 20:
    print("Nice.")
else:
    print("Cool.")

12. Summary and Next Steps

Here is what you learned:

Each concept was motivated by a real limitation of the previous one. That is not a teaching trick – that is how programming actually works. You reach for new tools when old ones are not enough.

Where to go next:

© 2026 Dominik Mueller   •  Powered by Soopr   •  Theme  Moonwalk