144 lines
4.8 KiB
Markdown
144 lines
4.8 KiB
Markdown
# Python – Cover interesting aspects
|
||
|
||
---
|
||
|
||
## 1. Syntax-Philosophie
|
||
|
||
Python ist eine sehr flexible Sprache – sie ist eine Skriptsprache, die Klassen und OOP unterstützt, aber nicht erzwingt. Im Gegensatz zu Java, wo man ohne Klassen nicht arbeiten kann, kann man in Python auf Klassen verzichten, wenn sie nicht gebraucht werden. Das spart viel Aufwand.
|
||
|
||
### Indentation
|
||
|
||
In Python ist Indentation von großem Wert. In vielen Sprachen (wie Java) kann der Compiler den Code dank der geschweiften Klammern auch ohne Einrückung lesen – trotzdem schreiben Entwickler immer mit Indentation, weil sie sonst ihren eigenen Code nicht verstehen würden.
|
||
|
||
Deshalb wurde Python so entwickelt: Man verzichtet auf geschweifte Klammern und strukturiert den Code ausschließlich über Einrückungen.
|
||
|
||
**Vorteile:**
|
||
- Alle Python-Codes sind lesbar (sonst erhält man einen `IndentationError`)
|
||
- Saubere, einheitliche Codestruktur
|
||
- Man vergisst keine schließende Klammer mehr
|
||
|
||
---
|
||
|
||
## 2. Dynamic & Duck Typing
|
||
|
||
In Python hat eine Variable keinen festen Typ – anders als in Java, wo man den Typ immer explizit deklarieren muss. In Python ist eine Variable nur ein **Label**, aber ihr **Wert** hat den Typ. Das nennt man **Dynamic Typing** oder **Duck Typing**.
|
||
|
||
> *„If it walks like a duck and quacks like a duck, it's a duck."*
|
||
|
||
Das bedeutet: Es kommt nicht auf den Typ eines Objekts an, sondern darauf, ob es die erwarteten Methoden besitzt.
|
||
|
||
```python
|
||
class Duck:
|
||
def quack(self):
|
||
print("Quack!")
|
||
|
||
class Person:
|
||
def quack(self):
|
||
print("I'm a Duck! (Quack)")
|
||
|
||
def make_it_quack(obj):
|
||
obj.quack()
|
||
|
||
make_it_quack(Duck())
|
||
make_it_quack(Person())
|
||
```
|
||
|
||
Beide Objekte werden akzeptiert, weil beide eine `quack()`-Methode haben – unabhängig von ihrem Typ.
|
||
|
||
---
|
||
|
||
## 3. List Comprehensions
|
||
|
||
Anstatt klassische `for`-Schleifen und `if`-Abfragen zu schreiben, unterstützt Python **List Comprehensions** – eine kompakte Syntax, um `for` und `if` in einer einzigen Zeile zusammenzufassen.
|
||
|
||
**Vorteile:**
|
||
- Sehr gut lesbar – fast wie ein englischer Satz
|
||
- Höhere Performance, da Comprehensions schneller als klassische Schleifen sind
|
||
- Kompakter als Java Streams, die ein ähnliches Konzept bieten
|
||
|
||
```python
|
||
arr = [1, 2, 3, 4, 5]
|
||
comprehensions = [n * n for n in arr if n % 2 == 0]
|
||
print(comprehensions) # [4, 16]
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Decorators
|
||
|
||
Ein **Decorator** ist eine Funktion, die eine andere Funktion entgegennimmt, neue Funktionalität hinzufügt und sie dann zurückgibt – ohne den ursprünglichen Code zu verändern.
|
||
|
||
Ein klassisches Beispiel ist ein **Timer-Decorator**: Statt am Anfang und Ende jeder Methode manuell die Zeit zu messen, schreibt man einmal `@timer` über die Methode.
|
||
|
||
Das sieht ähnlich aus wie **Annotationen in Java**, der entscheidende Unterschied ist aber: In Python haben Decorators einen echten Einfluss auf die Funktion – sie sind kein bloßes Metadaten-Tag. Man spricht hier von **Syntax Sugar**, denn im Hintergrund macht Python folgendes:
|
||
|
||
```python
|
||
heavy_calculation = timer(heavy_calculation)
|
||
```
|
||
|
||
**Beispiel:**
|
||
|
||
```python
|
||
import time
|
||
|
||
def timer(func):
|
||
def wrapper():
|
||
start = time.time()
|
||
func()
|
||
end = time.time()
|
||
print(f"Executed in {end - start} seconds")
|
||
return wrapper
|
||
|
||
@timer
|
||
def beispiel():
|
||
print("ist nur ein Beispiel")
|
||
time.sleep(2)
|
||
|
||
beispiel()
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Magic Methods (Dunder Methods)
|
||
|
||
**Magic Methods** (auch **Dunder Methods** genannt) ermöglichen es, das Verhalten von Objekten gezielt zu steuern. Der Name „Dunder" kommt von **Double Underscore** – sie werden mit `__` vor und nach dem Namen geschrieben, z. B. `__init__`.
|
||
|
||
Besonders mächtig: Man kann damit **Operatoren überladen**. Java erlaubt nur das Addieren von Strings mit `+`. Python hingegen bietet `__add__`, um beliebige zwei Objekte zu addieren.
|
||
|
||
**Beispiel – Punkt-Addition in Java vs. Python:**
|
||
|
||
In Java muss man explizit eine Methode aufrufen:
|
||
```java
|
||
Point p3 = p1.add(p2);
|
||
```
|
||
|
||
In Python genügt es, `__add__` zu definieren – den Rest übernimmt Python:
|
||
```python
|
||
class Point:
|
||
def __init__(self, x, y):
|
||
self.x = x
|
||
self.y = y
|
||
|
||
def __add__(self, other):
|
||
return Point(self.x + other.x, self.y + other.y)
|
||
|
||
def __str__(self):
|
||
return f"Point({self.x}, {self.y})"
|
||
|
||
p1 = Point(1, 2)
|
||
p2 = Point(3, 4)
|
||
p3 = p1 + p2
|
||
print(p3) # Point(4, 6)
|
||
```
|
||
|
||
### Übersicht häufiger Magic Methods
|
||
|
||
| Ausdruck | Magic Method | Beschreibung |
|
||
|------------|----------------------|--------------------|
|
||
| `a + b` | `a.__add__(b)` | Addition |
|
||
| `a - b` | `a.__sub__(b)` | Subtraktion |
|
||
| `a * b` | `a.__mul__(b)` | Multiplikation |
|
||
| `a == b` | `a.__eq__(b)` | Gleichheit |
|
||
| `a < b` | `a.__lt__(b)` | Kleiner als |
|
||
| `len(a)` | `a.__len__()` | Länge |
|
||
| `print(a)` | `a.__str__()` | String-Darstellung | |