# Python

### Ein kurzer Vortrag

## Einige Fakten

- Erfinder: Guido van Rossum
- Erste Version: Anfangs 1990
- Lizenz: Opensource, PSFL

## Einige Datentypen

### Listen

    >>> a = ['spam', 'eggs', 100, 1234]
    >>> a
    ['spam', 'eggs', 100, 1234]
    >>> a[0]
    'spam'
    >>> a[3]
    1234
    >>> a[-2]
    100
    >>> a[2] = a[2] + 23
    >>> a
    ['spam', 'eggs', 123, 1234]

### Dictonaries

    >>> tel = {'paul':57757, 'barbara':45304}
    >>> tel['guido'] = 89003
    >>> tel
    {'barbara': 45304, 'guido': 89003, 'paul': 57757}
    >>> tel['barbara']
    45304
    >>> tel.keys()
    ['paul', 'barbara', 'guido']
    >>> 'guido' in tel
    True

### Sets

    >>> korb = ['Apfel', 'Orange', 'Apfel', 'Birne', 'Orange', 'Banane']
    >>> frucht = set(korb)
    >>> frucht
    set(['Apfel', 'Banane', 'Birne', 'Orange'])
    >>> 'Orange' in frucht
    True
    >>> 'Kiwi' in frucht
    False

### Sets zum zweiten

So macht Mengenlehre Spass...

    >>> a = set('abracadabra')
    >>> b = set('alacazam')
    >>> a        # Buchstaben in a
    set(['a', 'r', 'b', 'c', 'd'])
    >>> a - b    # ... die es in a gibt aber nicht in b
    set(['r', 'd', 'b'])
    >>> a | b    # ... von a und b
    set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
    >>> a & b    # ... die in beiden vorhanden sind
    set(['a', 'c'])
    >>> a ^ b    # ... in a oder b, aber nicht in beiden
    set(['r', 'd', 'b', 'm', 'z', 'l'])

## Python Stärken

- Anfängerfreundlich, aber kein Spielzeug
- Batterien inbegriffen
- Objektorientiert
- Interaktiv
- Rapidprototyping
- Embeddable
- Portierbar
- Schnittstellen zu: C, C++, Objectiv-C, COM, DCOM, Java, .Net, ...

## Python Schwächen

- Performance
- GIL
- Python 2 vs. Python 3

## Software Entwicklungsprozess

1. Mach, dass es korrekt läuft
2. Teste ob es richtig läuft
3. Läuft es schnell genug?
4. Falls nicht: Optimieren
5. Zurück zum zweiten Punkt

## Fibonacci

Die Fibonacci-Folge lautet:

- Die erste Zahl ist 0
- Die zweite Zahl ist 1
- Jede weitere Zahl ist die Summe ihrer beiden Vorgänger

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    def fib(n):
        if n == 0: return 0
        if n == 1: return 1
        return fib(n - 1) + fib(n - 2)

    print fib(10)

    $ python fib.py
    55
    $ chmod +x fib.py
    $ ./fib.py
    55

## Tests mit Doctest

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    def fib(n):
        """Berechnet die n-te Zahl der Fibonacci-Folge

        >>> fib(0)
        0
        >>> fib(1)
        1
        >>> fib(10)
        55
        """
        if n == 0: return 0
        if n == 1: return 1
        return fib(n - 1) + fib(n - 2)

    print fib(10)

    $ python -m doctest -v fib.py
    Trying:
        fib(0)
    Expecting:
        0
    ok
    ...
    3 passed and 0 failed.
    Test passed.

## Performance

Der selbe Code, aber diesmal berechnen wir fib(30).

Um die Zeit zu messen starten wir das Programm mit dem Profiler.

    $ python -m cProfile fib.py
    832040
             2692539 function calls ... in 9.859 seconds

    ...

Sehr langsam. Fast 10 Sekunden für die 30-igte Fibonacci Zahl!

## Performance verbessern per C-Extension

Fibonacci in C implementiert:

    long fib(int);

    long fib(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        return fib(n - 1) + fib(n - 2);
    }

Als Library kompilieren:

    $ gcc -shared -Wl,-soname,libfib -o libfib.so -fPIC libfib.c

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """Berechnet die n-te Zahl der Fibonacci-Folge

    >>> lib.fib(0)
    0
    >>> lib.fib(1)
    1
    >>> lib.fib(10)
    55
    """

    import ctypes
    lib = ctypes.CDLL('./libfib.so')

    print lib.fib(30)

Super Schnell: nur noch 0.065
Aber was ist mit grösseren Fibonacci zahlen?

Tipp: Schon im ersten Programm steckt der Wurm drinn.

## Profiling des Algorithmus

Wieso ist der Code so langsam bei grösseren Zahlen? Eine einfache Analyse.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    def fib(n):
        "..."
        print "fib wurde mit n =", n, "aufgerufen"
        if n == 0: return 0
        if n == 1: return 1
        result = fib(n - 1) + fib(n - 2)
        return result

    fib(10)

    $ python fib.py
    fib wurde mit n = 10 aufgerufen
    fib wurde mit n = 9 aufgerufen
    fib wurde mit n = 8 aufgerufen
    fib wurde mit n = 7 aufgerufen
    ...
    fib wurde mit n = 0 aufgerufen
    fib wurde mit n = 1 aufgerufen
    fib wurde mit n = 4 aufgerufen
    fib wurde mit n = 3 aufgerufen
    fib wurde mit n = 2 aufgerufen
    fib wurde mit n = 1 aufgerufen
    fib wurde mit n = 0 aufgerufen
    fib wurde mit n = 1 aufgerufen
    fib wurde mit n = 2 aufgerufen
    fib wurde mit n = 1 aufgerufen
    fib wurde mit n = 0 aufgerufen

Da wird fib() immer wieder mit den selben Parametern aufgerufen und neu berechnet.

## Fibonacci mit besserem Algorithmus

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    cache = {}

    def fib(n):
        "..."
        if (n in cache): return cache[n]

        if n == 0: return 0
        if n == 1: return 1
        result = fib(n - 1) + fib(n - 2)

        cache[n] = result
        return result

    print fib(30)

Das Zahl wird jetzt in weniger als 0.001 Sekunden berechnet :D

# ENDE

◄ ► ▲ ▼