You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
author: Sebastian Zug, Galina Rudolf, André Dietrich
3
+
author: Sebastian Zug, Galina Rudolf, André Dietrich, Volker Göhler
4
4
email: sebastian.zug@informatik.tu-freiberg.de
5
-
version: 1.0.0
5
+
version: 1.0.1
6
6
language: de
7
7
narrator: Deutsch Female
8
-
comment: Konzeptioneller Einstieg in die Objektorientierung anhand von Python — für Teilnehmer ohne C#-Hintergrund. Klassen, Objekte, Konstruktor, Methoden, Vererbung.
9
-
tags:
10
-
logo:
8
+
comment: Konzeptioneller Einstieg in die Objektorientierung anhand von Python -- für Teilnehmer ohne C#-Hintergrund. Klassen, Objekte, Konstruktor, Methoden, Vererbung.
Stellen Sie sich vor, Sie verwalten einen kleinen Bauernhof in einem Programm. Drei Tiere sollen gespeichert werden — jeweils mit Name, Geräusch und Alter.
### Erster Lösungsversuch: Dictionaries und Funktionen
96
+
97
+
Erster Lösungsversuch: Dictionaries und Funktionen
98
+
====================
111
99
112
100
Bevor wir Klassen einführen, schauen wir uns an, wie weit man mit den bisher bekannten Mitteln kommt. Wir bündeln die Daten zu einem Tier in einem **Dictionary** und schreiben eine Funktion, die darauf arbeitet:
Das ist schon deutlich besser. Aber drei Probleme bleiben:
129
124
130
-
1.**Keine Garantie, dass die Daten zusammenpassen.** Niemand hindert uns daran, ein Dictionary ohne `sound` zu bauen — der Fehler erscheint erst beim Aufruf.
125
+
1.**Keine Garantie, dass die Daten zusammenpassen.** Niemand hindert uns daran, ein Dictionary ohne `sound` zu bauen. Der Fehler erscheint erst beim Aufruf.
131
126
2.**Daten und Funktionen sind getrennt.** Wer das Dictionary benutzt, muss wissen, *welche* Funktionen dazu gehören. Das steht nirgends im Code.
132
-
3.**Keine Bauplan-Beschreibung.** Was ein „Tier" ist, ergibt sich nur indirekt aus den Schlüsseln, die zufällig benutzt werden.
127
+
3.**Keine Bauplan-Beschreibung.** Was ein „Tier" ist, ergibt sich nur indirekt aus den Keys, die benutzt werden.
133
128
134
129
> **Frage:** Wie könnte eine Sprache uns helfen, Daten und passende Funktionen *zusammenzuhalten* und einen *Bauplan* zu beschreiben, an den sich alle Tiere halten müssen?
In Python schreiben wir den Bauplan mit dem Schlüsselwort `class`:
169
165
@@ -183,7 +179,7 @@ class Animal:
183
179
Drei Bestandteile sind neu und brauchen eine Erklärung:
184
180
185
181
-**`__init__`** — der **Konstruktor**. Wird *einmal* beim Erzeugen eines Objekts aufgerufen. Hier werden die Attribute initialisiert.
186
-
-**`self`** — der Verweis auf *dieses konkrete Objekt*. (In C# heißt das später `this`.) Er muss als erster Parameter jeder Methode stehen.
182
+
-**`self`** — der Verweis auf *dieses konkrete Objekt*. (In C# heißt es `this`.) Er muss als erster Parameter jeder Methode stehen. Ist aber nur eine Konvention.
187
183
-**`self.name = name`** — legt das Attribut `name`*am Objekt* ab. Ohne `self.` wäre `name` nur eine lokale Variable in der Funktion.
188
184
189
185
### Objekte erzeugen und nutzen
@@ -244,10 +240,11 @@ kitty.have_birthday()
244
240
245
241
### Klassenattribute vs. Instanzattribute
246
242
247
-
Manche Eigenschaften gehören *zu jedem einzelnen Objekt* (jedes Tier hat einen anderen Namen). Andere Eigenschaften gehören *zur ganzen Klasse* — sie sind für alle Tiere gleich. Beispiel: Alle Tiere auf dem Planeten leben auf demselben Planeten.
243
+
Manche Eigenschaften gehören *zu jedem einzelnen Objekt* (jedes Tier hat einen anderen Namen). Andere Eigenschaften gehören *zur ganzen Klasse* — sie sind für alle Tiere gleich. Beispiel: Alle Tiere atmen Luft und leben auf demselben Planeten.
248
244
249
245
```python ClassAttribute.py
250
246
classAnimal:
247
+
atmen ="Luft"# Klassenattribut — gehört zur Klasse
251
248
planet ="Erde"# Klassenattribut — gehört zur Klasse
> **Hintergrund:** Methoden, deren Namen in `__` eingerahmt sind (man spricht „Dunder-Methoden"), sind Haken, die Python automatisch aufruft. Wir werden in C# später ähnliche Mechanismen sehen — dort heißen sie `ToString()`, `Equals()` etc.
312
+
> **Hintergrund:** Methoden, deren Namen in `__` eingerahmt sind (man spricht „Dunder-Methoden"), sind Hooks, die Python automatisch aufruft. Wir werden in C# später ähnliche Mechanismen sehen — dort heißen sie `ToString()`, `Equals()` etc.
316
313
317
314
## Mehrere Objekte zusammenführen
318
315
@@ -356,7 +353,8 @@ farm.morning_call()
356
353
357
354
## Kapselung — Was darf nach außen sichtbar sein?
358
355
359
-
### Das Problem
356
+
Das Problem
357
+
====================
360
358
361
359
Bisher konnten wir auf jedes Attribut von außen direkt zugreifen — auch schreibend. Das ist gefährlich:
362
360
@@ -406,17 +404,77 @@ print(f"{kitty.name} ist {kitty.get_age()} Jahre alt.")
> **Merke:** Kapselung trennt das *Was* (öffentliches Verhalten) vom *Wie* (interne Umsetzung). Wer die Klasse benutzt, soll nur das Was sehen müssen — wir können das Wie ändern, ohne dass Aufrufer kaputt gehen.
414
412
415
-
> In C# werden wir dafür echte Modifizierer (`public`, `private`) und sogenannte **Properties** kennenlernen — Vorlesung 07/08.
413
+
> In C# werden wir dafür echte Modifizierer (`public`, `private`) und sogenannte **Properties** kennenlernen — Vorlesung 07/08. Das geht in Python auch — siehe nächster Slide.
414
+
415
+
### Python Properties: Elegante Kapselung mit `@property`
416
+
417
+
In Python können **Properties** verwendet werden, um den Zugriff auf Attribute zu kontrollieren, ohne die Syntax für den Zugriff zu ändern. Dies ermöglicht eine saubere Trennung zwischen öffentlicher Schnittstelle und interner Implementierung.
418
+
419
+
```python EncapsulationProperties.py
420
+
classAnimal:
421
+
def__init__(self, name, age):
422
+
self.name = name
423
+
self._age = age # Internes Attribut
424
+
425
+
@property
426
+
defage(self):
427
+
"""Getter für das Alter."""
428
+
returnself._age
429
+
430
+
@age.setter
431
+
defage(self, new_age):
432
+
"""Setter für das Alter mit Validierung."""
433
+
if new_age <0:
434
+
raiseValueError(f"Ungültiges Alter: {new_age}")
435
+
self._age = new_age
436
+
437
+
@age.deleter
438
+
defage(self):
439
+
"""Löscht das Alter."""
440
+
print(f"Alter von {self.name} wird gelöscht.")
441
+
delself._age
442
+
443
+
# Verwendung
444
+
kitty = Animal("Kitty", 5)
445
+
print(f"{kitty.name} ist {kitty.age} Jahre alt.") # Getter
|`@property`| Definiert den **Getter** für ein Attribut. |
464
+
|`@<property>.setter`| Definiert den **Setter** für ein Attribut (mit Validierung). |
465
+
|`@<property>.deleter`| Definiert den **Deleter** für ein Attribut. |
466
+
467
+
---
468
+
> **Vorteil von Properties:**
469
+
>
470
+
> - Der Zugriff auf Attribute bleibt **intuitiv** (`obj.age` statt `obj.get_age()`).
471
+
> - Die **Validierung** und **Logik** bleiben in der Klasse verborgen.
472
+
> - Änderungen an der internen Implementierung (z. B. Berechnung von `_age`) haben **keine Auswirkungen** auf den Code, der die Klasse nutzt.
416
473
417
474
## Vererbung
418
475
419
-
### Motivation
476
+
Motivation
477
+
====================
420
478
421
479
Auf unserem Bauernhof sollen verschiedene Tierarten leben. Ein **Hund** kann zusätzlich `fetch()` (Stöckchen apportieren), eine **Kuh** liefert `milk()`. Beide sind aber *immer noch Tiere* — sie haben Namen, Alter, Geräusch.
422
480
@@ -490,6 +548,8 @@ class Cat(Animal):
490
548
# Wir überschreiben die Methode aus Animal
491
549
print(f"{self.name} macht {self.sound} ... und schnurrt.")
492
550
551
+
animal = Animal("Berta", "Muuh", 8)
552
+
animal.make_noise() # Berta macht Muuh
493
553
494
554
kitty = Cat("Kitty", "Miau", 5)
495
555
kitty.make_noise()
@@ -532,7 +592,7 @@ kitty.make_noise()
532
592
533
593
### Eine gemeinsame Liste verschiedener Tiere
534
594
535
-
Weil`Dog` und `Cat`*beide*`Animal`s sind, dürfen sie in derselben Liste stehen — und beim Aufruf von `make_noise()` reagiert jedes auf seine eigene Art.
595
+
Da`Dog` und `Cat`*beide*`Animal`s sind, haben sie die `make_noise` Methode und wir können diese aufrufen und erwarten, dass jedes Tier das passende Geräusch macht.
536
596
537
597
```python Polymorphism.py
538
598
classAnimal:
@@ -633,12 +693,33 @@ Zum Abschluss verbinden wir alle Konzepte in einem etwas größeren Beispiel. Ei
633
693
classProduct:
634
694
def__init__(self, name, price, stock):
635
695
self.name = name
636
-
self._price = price #intern, über Methoden zugreifen
637
-
self._stock = stock
696
+
self._price = price #Intern, über Property zugreifen
697
+
self._stock = stock# Intern, über Property zugreifen
@@ -758,6 +836,7 @@ In den Vorlesungen 07, 08 und 09 gehen wir tiefer:
758
836
6.**Hierarchie modellieren.** Modellieren Sie eine Klassenhierarchie für Verkehrsmittel: `Vehicle` (mit `name` und Methode `move()`), darunter `Car`, `Bicycle`, `Boat`. Jede Klasse soll `move()` passend überschreiben (z. B. „rollt", „tritt", „schwimmt"). Legen Sie eine Liste aus drei verschiedenen Verkehrsmitteln an und rufen Sie `move()` in einer Schleife auf.
759
837
760
838
7.**Quizfragen zur Selbstkontrolle.**
839
+
761
840
- Was ist der Unterschied zwischen einer Klasse und einem Objekt?
762
841
- Warum ist `self` als erster Parameter jeder Methode notwendig?
763
842
- Wann verwenden Sie Vererbung, wann eher Komposition?
0 commit comments