Python Crash Course

Kapitel 9

Klassen

Objektorientierte Programmierung

Was sind Klassen?

  • Blaupausen für Objekte
  • Bündeln Daten (Attribute) und Verhalten (Methoden)
  • Ermöglichen Wiederverwendung und Organisation
  • Grundlage der objektorientierten Programmierung
class Dog:
    """A simple attempt to model a dog."""
    
    def __init__(self, name, age):
        """Initialize name and age attributes."""
        self.name = name
        self.age = age
    
    def sit(self):
        """Simulate a dog sitting in response to a command."""
        print(f"{self.name} is now sitting.")
    
    def roll_over(self):
        """Simulate rolling over in response to a command."""
        print(f"{self.name} rolled over!")

Instanzen erstellen und verwenden

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def sit(self):
        print(f"{self.name} is now sitting.")
    
    def roll_over(self):
        print(f"{self.name} rolled over!")

# Instanzen erstellen
my_dog = Dog('Willie', 6)
your_dog = Dog('Lucy', 3)

# Attribute zugreifen
print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")

# Methoden aufrufen
my_dog.sit()
your_dog.roll_over()

🛠️ Übung 1: Erste Klassen

🔗 jupyter.gymnasium-hummelsbuettel.de

Aufgaben 9-1 bis 9-3:

  • 9-1: Restaurants - Klasse mit Methoden
  • 9-2: Drei Restaurants - mehrere Instanzen
  • 9-3: Benutzer - User-Klasse erstellen

→ kapitel_9_aufgaben_9-1_9-3.ipynb

↓ Lösungen

💡 Lösungen Übung 1 (Teil 1)

# 9-1: Restaurants
class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type
    
    def describe_restaurant(self):
        print(f"{self.restaurant_name} serves {self.cuisine_type} food.")
    
    def open_restaurant(self):
        print(f"{self.restaurant_name} is now open!")

restaurant = Restaurant("Mario's", "Italian")
print(f"Restaurant: {restaurant.restaurant_name}")
print(f"Cuisine: {restaurant.cuisine_type}")
restaurant.describe_restaurant()
restaurant.open_restaurant()

# 9-2: Drei Restaurants
restaurant1 = Restaurant("Sushi Bar", "Japanese")
restaurant2 = Restaurant("Curry House", "Indian")
restaurant3 = Restaurant("Pizza Palace", "Italian")

restaurant1.describe_restaurant()
restaurant2.describe_restaurant()
restaurant3.describe_restaurant()

📄 Ausgabe - Übung 1 Teil 1

Restaurant: Mario's
Cuisine: Italian
Mario's serves Italian food.
Mario's is now open!
Sushi Bar serves Japanese food.
Curry House serves Indian food.
Pizza Palace serves Italian food.

💡 Lösungen Übung 1 (Teil 2)

# 9-3: Benutzer
class User:
    def __init__(self, first_name, last_name, age, city):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.city = city
    
    def describe_user(self):
        print(f"\nUser Summary:")
        print(f"Name: {self.first_name} {self.last_name}")
        print(f"Age: {self.age}")
        print(f"City: {self.city}")
    
    def greet_user(self):
        print(f"Hello, {self.first_name}! Welcome back!")

user1 = User("Alice", "Smith", 25, "Hamburg")
user2 = User("Bob", "Jones", 30, "Berlin")

user1.describe_user()
user1.greet_user()

user2.describe_user()
user2.greet_user()

📄 Ausgabe - Übung 1 Teil 2


User Summary:
Name: Alice Smith
Age: 25
City: Hamburg
Hello, Alice! Welcome back!

User Summary:
Name: Bob Jones
Age: 30
City: Berlin
Hello, Bob! Welcome back!

Attribute ändern

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
    
    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")
    
    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")
    
    def increment_odometer(self, miles):
        self.odometer_reading += miles

my_car = Car('audi', 'a4', 2019)
print(my_car.get_descriptive_name())

# Attribut direkt ändern
my_car.odometer_reading = 23
my_car.read_odometer()

# Attribut über Methode ändern
my_car.update_odometer(23500)
my_car.read_odometer()

🛠️ Übung 2: Attribute ändern

Aufgaben 9-4 und 9-5:

  • 9-4: Anzahl der Gäste - Restaurant erweitern
  • 9-5: Anmeldeversuche - User erweitern

→ kapitel_9_aufgaben_9-4_9-5.ipynb

↓ Lösungen

💡 Lösung 9-4 (Teil 1)

# 9-4: Anzahl der Gäste - Restaurant Klasse
class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type
        self.number_served = 0
    
    def describe_restaurant(self):
        print(f"{self.restaurant_name} serves {self.cuisine_type} food.")
    
    def open_restaurant(self):
        print(f"{self.restaurant_name} is now open!")
    
    def set_number_served(self, number):
        self.number_served = number
    
    def increment_number_served(self, additional_served):
        self.number_served += additional_served

💡 Lösung 9-4 (Teil 2)

# Restaurant verwenden
restaurant = Restaurant("Mario's", "Italian")
print(f"Number served: {restaurant.number_served}")

restaurant.number_served = 50
print(f"Number served: {restaurant.number_served}")

restaurant.set_number_served(100)
restaurant.increment_number_served(25)
print(f"Number served: {restaurant.number_served}")

📄 Ausgabe 9-4

Number served: 0
Number served: 50
Number served: 125

💡 Lösung 9-5 (Teil 1)

# 9-5: Anmeldeversuche - User Klasse
class User:
    def __init__(self, first_name, last_name, age, city):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.city = city
        self.login_attempts = 0
    
    def describe_user(self):
        print(f"\nUser: {self.first_name} {self.last_name}")
        print(f"Age: {self.age}, City: {self.city}")
    
    def greet_user(self):
        print(f"Hello, {self.first_name}!")
    
    def increment_login_attempts(self):
        self.login_attempts += 1
    
    def reset_login_attempts(self):
        self.login_attempts = 0

💡 Lösung 9-5 (Teil 2)

# User verwenden
user = User("Alice", "Smith", 25, "Hamburg")
user.increment_login_attempts()
user.increment_login_attempts()
user.increment_login_attempts()
print(f"Login attempts: {user.login_attempts}")

user.reset_login_attempts()
print(f"Login attempts: {user.login_attempts}")

📄 Ausgabe 9-5

Login attempts: 3
Login attempts: 0

Vererbung

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
    
    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()
    
    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")

class ElectricCar(Car):
    """Represent aspects of a car, specific to electric vehicles."""
    
    def __init__(self, make, model, year):
        """Initialize attributes of the parent class."""
        super().__init__(make, model, year)
        self.battery_size = 75
    
    def describe_battery(self):
        """Print a statement describing the battery size."""
        print(f"This car has a {self.battery_size}-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

Instanzen als Attribute (Teil 1)

class Battery:
    """A simple attempt to model a battery for an electric car."""
    
    def __init__(self, battery_size=75):
        """Initialize the battery's attributes."""
        self.battery_size = battery_size
    
    def describe_battery(self):
        """Print a statement describing the battery size."""
        print(f"This car has a {self.battery_size}-kWh battery.")
    
    def get_range(self):
        """Print a statement about the range this battery provides."""
        if self.battery_size == 75:
            range = 260
        elif self.battery_size == 100:
            range = 315
        
        print(f"This car can go about {range} miles on a full charge.")

Instanzen als Attribute (Teil 2)

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

# Verwendung
my_tesla = ElectricCar('tesla', 'model s', 2019)
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

🛠️ Übung 3: Vererbung

Aufgaben 9-6 bis 9-9:

  • 9-6: Eisdiele - Restaurant erweitern
  • 9-7: Admin - User erweitern
  • 9-8: Berechtigungen - eigene Klasse
  • 9-9: Batterie-Upgrade - Methoden erweitern

→ kapitel_9_aufgaben_9-6_9-9.ipynb

↓ Lösungen

💡 Lösung 9-6

# 9-6: Eisdiele
class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type

class IceCreamStand(Restaurant):
    def __init__(self, restaurant_name, cuisine_type):
        super().__init__(restaurant_name, cuisine_type)
        self.flavors = ['vanilla', 'chocolate', 'strawberry']
    
    def show_flavors(self):
        print("Available flavors:")
        for flavor in self.flavors:
            print(f"- {flavor}")

ice_cream_stand = IceCreamStand("Sweet Dreams", "Ice Cream")
ice_cream_stand.show_flavors()

💡 Lösung 9-7

# 9-7: Admin
class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

class Admin(User):
    def __init__(self, first_name, last_name):
        super().__init__(first_name, last_name)
        self.privileges = ["can add post", "can delete post", "can ban user"]
    
    def show_privileges(self):
        print("Admin privileges:")
        for privilege in self.privileges:
            print(f"- {privilege}")

admin = Admin("Alice", "Admin")
admin.show_privileges()

📄 Ausgabe 9-6

Available flavors:
- vanilla
- chocolate
- strawberry

📄 Ausgabe 9-7

Admin privileges:
- can add post
- can delete post
- can ban user

💡 Lösungen Übung 3 (Teil 2)

# 9-8: Berechtigungen
class Privileges:
    def __init__(self):
        self.privileges = ["can add post", "can delete post", "can ban user"]
    
    def show_privileges(self):
        print("Admin privileges:")
        for privilege in self.privileges:
            print(f"- {privilege}")

class Admin(User):
    def __init__(self, first_name, last_name):
        super().__init__(first_name, last_name)
        self.privileges = Privileges()

admin = Admin("Bob", "Admin")
admin.privileges.show_privileges()

# 9-9: Batterie-Upgrade
class Battery:
    def __init__(self, battery_size=75):
        self.battery_size = battery_size
    
    def get_range(self):
        if self.battery_size == 75:
            range = 260
        elif self.battery_size == 100:
            range = 315
        print(f"Range: about {range} miles")
    
    def upgrade_battery(self):
        if self.battery_size != 100:
            self.battery_size = 100

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar('tesla', 'model s', 2019)
my_tesla.battery.get_range()
my_tesla.battery.upgrade_battery()
my_tesla.battery.get_range()

📄 Ausgabe - Übung 3 Teil 2

Admin privileges:
- can add post
- can delete post
- can ban user
Range: about 260 miles
Range: about 315 miles

Klassen aus Modulen importieren

# car.py
class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0
    
    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

# my_car.py
from car import Car

my_new_car = Car('audi', 'a4', 2019)
print(my_new_car.get_descriptive_name())

# Alternative Import-Methoden:
from car import Car, ElectricCar
import car
from car import Car as C
from car import *

Python-Standardbibliothek

from random import randint, choice

class Die:
    """A class representing a single die."""
    
    def __init__(self, num_sides=6):
        """Assume a six-sided die."""
        self.num_sides = num_sides
    
    def roll(self):
        """Return a random value between 1 and number of sides."""
        return randint(1, self.num_sides)

# Würfel verwenden
die = Die()
results = []
for roll_num in range(10):
    result = die.roll()
    results.append(result)
print(results)

# Lotterie
possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'a', 'b', 'c', 'd', 'e']
winning_ticket = []
for i in range(4):
    pulled_item = choice(possibilities)
    winning_ticket.append(pulled_item)

print(f"Winning ticket: {winning_ticket}")

🛠️ Übung 4: Module und Bibliotheken

Aufgaben 9-10 bis 9-16:

  • 9-10/11/12: Klassen importieren
  • 9-13: Würfel - random verwenden
  • 9-14/15: Lotterie und Analyse
  • 9-16: Python Module of the Week

→ kapitel_9_aufgaben_9-10_9-12.ipynb und 9-13_9-16.ipynb

↓ Lösungen

💡 Lösungen Übung 4 (Teil 1)

# 9-13: Würfel
from random import randint

class Die:
    def __init__(self, sides=6):
        self.sides = sides
    
    def roll_die(self):
        return randint(1, self.sides)

# 6-seitiger Würfel
six_sided = Die()
print("6-sided die:")
for i in range(10):
    print(six_sided.roll_die())

# 10-seitiger Würfel
ten_sided = Die(10)
print("\n10-sided die:")
for i in range(10):
    print(ten_sided.roll_die())

# 20-seitiger Würfel
twenty_sided = Die(20)
print("\n20-sided die:")
for i in range(10):
    print(twenty_sided.roll_die())

📄 Ausgabe - Übung 4 Teil 1

6-sided die:
3
1
6
2
4
5
1
6
3
2

10-sided die:
7
2
10
5
1
8
9
3
6
4

20-sided die:
15
8
12
3
19
7
11
20
5
14

💡 Lösungen Übung 4 (Teil 2)

# 9-14: Lotterie
from random import choice

def get_winning_ticket(possibilities):
    """Return a winning ticket from a list of possibilities."""
    winning_ticket = []
    for i in range(4):
        pulled_item = choice(possibilities)
        winning_ticket.append(pulled_item)
    return winning_ticket

possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'a', 'b', 'c', 'd', 'e']
winning_ticket = get_winning_ticket(possibilities)
print(f"Any ticket matching {winning_ticket} wins a prize!")

# 9-15: Lotterieanalyse
my_ticket = [1, 2, 'a', 'b']
plays = 0
won = False

while not won:
    new_ticket = get_winning_ticket(possibilities)
    plays += 1
    if new_ticket == my_ticket:
        won = True

print(f"It took {plays} plays to win the lottery!")
print(f"Your ticket: {my_ticket}")
print(f"Winning ticket: {new_ticket}")

📄 Ausgabe - Übung 4 Teil 2

Any ticket matching [7, 'c', 2, 9] wins a prize!
It took 243785 plays to win the lottery!
Your ticket: [1, 2, 'a', 'b']
Winning ticket: [1, 2, 'a', 'b']

Zusammenfassung Kapitel 9

  • Klassen: Blaupausen für Objekte
  • __init__(): Konstruktor-Methode
  • Vererbung: Klassen erweitern
  • super(): Parent-Klasse aufrufen
  • Komposition: Instanzen als Attribute
  • Module: Klassen organisieren

Praktische Tipps

  • Klassennamen: PascalCase verwenden
  • Methodennamen: snake_case verwenden
  • Docstrings: Klassen und Methoden dokumentieren
  • Vererbung: Nur bei "ist-ein" Beziehungen
# Gute Klassennamen
class ElectricCar:  # PascalCase
    def get_range(self):  # snake_case
        """Calculate the range of the car."""  # Docstring
        pass

# Vererbung richtig verwenden
class Dog(Animal):  # Dog "ist ein" Animal
    pass