Ziele der Übungen

  • Einüben der gelernten Inhalte
  • Entwicklung von Problemlösungsfähigkeiten beim Programmieren
    • Was ist zu tun -> Wie kann/sollte/muss der Code dazu aussehen?
    • Erkennen wo Probleme/Fehler auftreten könnten und wie diese vermieden werden können? (e.g. falsche Ergebnisse)
    • Abstraktion, wiederverwendbarer (“sauberer”) Code
  • Einhaltung von Standards und Best-Pratice Empfehlungen
    • Code Style und Syntax
    • Dokumentation
    • Lesbarkeit

Allgemeine Auffälligkeiten

Oft gute/funktionierende Lösungen aber es hakt häufig im Detail…

Was sollte in der Lösung enthalten sein?
  • Alle für die Lösung notwendigen/geforderten Dateien und Verzeichnisse, nicht mehr und nicht weniger.
    • notwendig: die Lösung muss funktionieren (Bsp. wenn eine Datei “extra_funktion.py” eine in der Lösung verwendete Funktion hallo_welt enthält und nicht abgegeben wird -> 💣)
    • gefordert: die Aufgabe erfordert die Erstellung einer Datei -> Siehe folgende Folie
  • Dokumentation:
    • inline: wenn nicht auf den ersten Blick erkennbar ist warum die Lösung so gewählt wurde/was der code tut
    • Funktionen

Was gehört nicht in die Lösung?

  • Test Skripte
  • Alternative Lösungen
  • verschiedene Versionen der selben Datei

=> Wie sollen wir wissen welche Lösung bewertet werden soll?

Pfade

Relative Pfade

Ihr solltet wenn immer möglich relative Pfade verwenden.

Pfade ähnlich wie folgender (Heinz Mustermann mit eigenenm Namen ersetzten)

datei_pfad = "C:/Users/Heinz_Mustermann/Documents/Python_Kurs/exercise-x/data/text_datei.txt"

wird in der Regel nur auf eurem eigenen Rechner existieren. Konkret heißt das eure Skripte laufen bei uns nicht => schlecht 💣

Im Skript haben wir euch auch schon Pfade vorbereitet diese dürft ihr auch nutzen. Sie nächster Absatz.

Pfade konstruieren

Folgendes war im Skript vorbereitet

from pathlib import Path

data_dir = Path("data")
output_dir = Path("solution")

Einen Pfad zu einer Datei “so_ist_es_richtig.txt” welche Ich im Ordner “solution”, welcher im selben Ordner wie das Skript liegt, speichern soll kann ich dann einfach wie folgt konstruieren:

pfad_zur_datei = output_dir / "so_ist_es_richtig.txt"

Warum geht das und warum kann ich auf einen absoluten Pfad wie im Beispiel oben verzichten? Vorausgesetzt Ich befinde mich im ordner vom Skript d.h. wenn Ich mein Skript mit:

python mein_skript.py

aufrufe, werden alle Pfade relativ zu diesem Ordner funktionieren.

Öffnen und lesen einer Datei

Wenn eine Datei geöffnet und gelesen werden soll gibt es zwei Möglichkeiten welche unten dargestellt sind. Empfehlenswert ist das with statement.

Der Grund ist das Resourcen, in diesem Fall geöffnete Dateien, wieder frei gegeben werden sollten wenn man dies nicht macht kann es im schlimmsten Fall zu memory leaks führen. Das with über nimmt das freigeben der Resourcen für euch (in diesem Fall das schließen der Datei) ist übersichtlich und gut lesbar.

Wenn man es ganz genau nimmt müsste im zweiten Fall wenn man die Datei manuell schließt sogar noch mit try except arbeiten da beim lesen ja etwas schief gehen kann und man troztdem sicher gehen möchte das die Datei wieder geschlossen wird. Das heißt der Code würde noch komplexer. Die erste Variante ist daher die deutlich einfachere. Falls ihr euch trotzdem für zweite Variante entscheidet solltet ihr aber mindestens die Datei schließen.

Für diejenigen die es ganz genau wissen wollen: PEP 343 gibt mehr Auskunft.

Variante 1:

with open("irgendeine_datei.txt", "r") as file:
    inhalt = file.read()
    
#mit inhalt kann jetzt weiter gearbeitet werden...
inhalt

Variante 2:

datei_pfad = "irgendeine_datei.txt"

file = open(datei_pfad, "r")

inhalt = file.read()

file.close()

#mit inhalt kann jetzt weiter gearbeitet werden...

Aufgabenstellung richtig lesen

Wenn die Aufgaben wie folgt ist:

#Write the counts to the file `counts.csv` in the `solution` directory in the format (first line is the header): [2P]
#item, count
#item_name_1, item_count_1
#item_name_2, item_count_2

Dann wünschen wir uns das die Datei mit der Lösung auch tatsächlich counts.csv heißt und im Ordner solution gespeichert ist und nicht im Ordner “solutions”, “ergebnis” oder im selben ordner wie das Skript und auch der Name nicht einer von den folgenden ist:

  • count.csv / UniqueCars_Count.csv / results_counting_occurences.csv / cars_frequency.csv / …

Das selbe gilt in diesem Beispiel für den Header der Datei: ganz formidabel wäre item, cout und nicht etwa eine folgender Varianten:

  • Type;count / model,countings / kein Header

Einhalten von Standards, Style und Lesbarkeit

Manche Dinge sind Sache der persönlichen Präferenz, wie z.B. die Wahl der Hochzeichen. Beide Varianten funktionieren.
Wichtig ist:
CHOOSE ONE AND STICK WITH IT

Siehe hierzu Python PEP8 code style.

Dort sind die “offiziell” von Python festgelegten coding guidelines beschrieben. Wenn Zweifel besteht wie man es machen soll kann man dort nachschauen. Mit den Empfehlungen macht man es sicher nicht falsch. Das gilt für viele der folgenden Beispiele

NEIN:

with open('datei.txt', "r") as file: 

JA:

with open("datei.txt", "r") as file: 

Unötige Leerzeichen

NEIN:

from pathlib import Path

datei_pfad = Path ("datei.txt")

with open (datei_pfad, "r") as file:
    inhalt = file.read ()

JA:

datei_pfad = Path("datei.txt")

with open(datei_pfad, "r") as file:
    inhalt = file.read()

Unötige Datentypen konversionen

Häufig wurden listen noch mal zu listen konvertiert:

obst = ["banane", "apfel", "birne"]
neue_liste = list(obst)

Bei pathlib Pfaden wurden unnötige String konversionen vorgenommen:
NEIN:

datei_pfad = Path("datei.txt")

with open(str(datei_pfad), "r") as file:
    ...

JA:

datei_pfad = Path("datei.txt")

with open(datei_pfad, "r") as file:
    ...

Unnötige Konversion von iterables zu listen:
NEIN:

unique_obst = list(set(obstliste))

for i in unique_obst:
    print(i)

JA:

unique_obst = set(obstliste)

for i in unique_obst:
    print(i)

Unnötige Code Redundanz

NEIN:

data_dir = Path("images")

if not Path("images").exists():
    Path("images").mkdir()

JA:

data_dir = Path("images")

if not data_dir.exists():
    data_dir.mkdir()

Funktionen, return, Dokumentation

In folgender Funktions Definition sind mehrere Sachen schief gegangen.

def irgendeine_funktion(x):
    """
    Beschreibung von irgendeiner Funktion
    
    Parameters
    ----------
    x : textdatei
        
    Returns
    -------
    ergebnis
    
    """
    
    print(x)
  1. Die Funktion hat keinen return value. Wer das nicht glaubt sollte folgendes ausprobieren:
    input_liste = [1, 2, 3]
    ergebnis = irgendeine_funktion(input_liste)
    

    und schauen was in der Variable Ergebnis gespeichert ist.

  2. Die Dokumentation hält sich nicht an den numpy Style (welcher es sein soll) oder an irgendeinen Style der Dokumentation. Das ändert zwar nichts an der Funktionalität selbst ist aber nicht konsistent mit der Einhaltung von Standards. Außerdem stimmen die Beschreibungen der Parameter nicht (return siehe 1.). Wenn die Funktion irgendetwas mit einer Liste machen soll die Ich ihr übergebe dann ist x vom Datentyp list und keine Textdatei.

Unten ist ein Beispiel wie es aussehen könnte.

Wenn man sich nicht sicher ist wie der Style aus zu sehen hat kann man z.B. unter numpy doc nachlesen (oder ggf. in der google style doku).

def irgendeine_funktion(x):
    """
    Beschreibung von irgendeiner Funktion
    
    Parameters
    ----------
    x : list
        ggf. Beschreibung von liste
        
    Returns
    -------
    list
        ggf. nähere Beschreibung von liste
    
    """
    
    return x
        
ergebnis = irgendeine_funktion(input_liste)