Dictonary als Vokabeltrainer

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DoPython
User
Beiträge: 1
Registriert: Donnerstag 9. November 2023, 11:40

folgenden Code gab ich ein :

Code: Alles auswählen

# vokabeltrainer.py
import random


#Funktionsdefinition
def dict_laden(pfad):
    d= {}
    try:
        datei = open(pfad)
        liste = datei.readlines()
        for eintag in liste:

            l_eintrag = eintrag.split()
            d[l_eintrag[0]] = l_eintrag[1:]

              datei.close()
              except:
                  pass
              return d


          def aufgabe(d):
              zufall = random.randint(0,lend(d.keys())-1)
              vokabel = list(d.keys())[zufall]
              print('Wie lautet das deutsche Wort für ', vokabel+'?')
              antwort = input()

              if antwort not in d[vokabel]:
                  print('Leider falsch.')
                  print(vokabel, 'bedeutet:', end='')
                  for wort in d[vokabel]:
                      print(wort,end='')
                      print()
                      else:

                          print('Richtig!')
                          del d[vokabel]


             #Hauptprogramm
             print('Vokabeltrainer')
             print()
             woerterbuch = dict_laden('"C:\Users\Birgit\source\repos\PythonDictonary\PythonDictonary\get.txt"')


                          while woerterbuch:
                              aufgabe (woerterbuch)
                              print('Sie haben alle Vokabeln gelernt.')
                              eingabe = input()
Diese Fehlermeldung erhielt ich: "Traceback (most recent call last):
File "C:\Users\Birgit\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Users\Birgit\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "c:\program files\microsoft visual studio\2022\community\common7\ide\extensions\microsoft\python\core\debugpy\__main__.py", line 45, in <module>
cli.main()
File "c:\program files\microsoft visual studio\2022\community\common7\ide\extensions\microsoft\python\core\debugpy/..\debugpy\server\cli.py", line 444, in main
run()
File "c:\program files\microsoft visual studio\2022\community\common7\ide\extensions\microsoft\python\core\debugpy/..\debugpy\server\cli.py", line 285, in run_file
runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
File "C:\Users\Birgit\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 288, in run_path
code, fname = _get_code_from_file(run_name, path_name)
File "C:\Users\Birgit\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 257, in _get_code_from_file
code = compile(f.read(), fname, 'exec')
File "C:\Users\Birgit\source\repos\PythonDictonary\PythonDictonary\PythonDictonary.py", line 17
datei.close()
IndentationError: unexpected indent
Press any key to continue . . ."


Woran liegt das?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Einrueckungen sind in Python wichtig. Das sieht bei dir eher frei Schnauze aus, und das darf's halt nicht sein. def aufgabe gehoert auf die oberste ebene, darin das if/else muss ebenfalls richtig sein, das Hauptprogramm muss auf die oberste Ebene, das while darin auch, etc.
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

DoPython hat geschrieben: Donnerstag 9. November 2023, 11:45
IndentationError: unexpected indent
Press any key to continue . . ."


Woran liegt das?
Python sagt dir, dass es mit deiner Einrückung nicht klarkommt. Wie mein Vorredner schon gesagt hat Formatiere dein Programm richtig und vielleicht geht es dann.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DoPython: Neben der Einrückung sind da noch ein paar mehr Fehler drin. Noch ein Syntaxfehler weil das ``\``-Zeichen in Zeichenkettenliteralen eine besondere Bedeutung hat und "\U" die Eingabe von Unicodezeichen mittels Codepoint ermöglicht, da dann aber auch tatsächlich der Codepoint im erwarteten Format folgen muss. Wenn man bei Zeichenketten beispielsweise mit Pfadangaben oder regulären Ausdrücken diese „Escape“-Funktion für ``\`` nicht haben möchte, kann man dem Zeichenkettenliteral ein kleines oder grosses R voranstellen, dann werden die Escape-Sequenzen nicht mehr besonder interpretiert und ``R"\U"`` ist eine Zeichenkette die tatsächlich die beiden Zeichen \ und U enthält.

Der Dateiname ist dann immer noch nicht richtig, weil da zusätzliche Anführungszeichen gesetzt sind. Dann gibt es noch mindestens an zwei Stellen Vertipper bei Namen und die Ausgabe bei falschen Benutzereingaben ist so sicher nicht ganz gewollt.

Die Fehler sollten so eigentlich gar nicht alle parallel existieren dürfen, wenn man das Programm entwickelt und nicht erst alles runterschreibt, und danach erst schaut ob das läuft. Entwickeln heisst eine Funktion ganz oder auch nur teilweise zu schreiben bis sie in einem Zustand ist, den man testen kann. Das macht man, und nur wenn die Funktion die Tests besteht, macht man mit der nächsten Funktion weiter, beziehungsweise mit dem nächsten Schritt in der aktuellen Funktion, falls die noch nicht fertig ist.

Die Ausnahmebehandlung in `dict_laden()` ist fehlerhaft. Man sollte nie einfach so *alle* Ausnahmen behandeln, wenn man das gar nicht sinnvoll kann. Schon gar nicht in dem man die Ausnahme dann gar nicht behandelt, sondern einfach ignoriert. Wenn der Pfad falsch ist/die Datei nicht existiert, dann wird dem Benutzer einfach nur ausgegeben, dass der alle Vokabeln gelernt hat. Das ist sicher nicht sinnvoll. Falls die Datei leerzeichen enthält, bricht das einlesen an dieser Leerzeile ab, ohne das der Aufrufer der Funktion das irgendwie mitbekommt. Ich würde sagen die Ausnahmebehandlung ist in diesem Programm an der Stelle einfach grundsätzlich falsch und ich würde die komplett entfernen.

Ein ``pass`` in einem ``except:``-Zweig ist in der Regel eine gute Gelegenheit für einen Kommentar warum das okay ist, oder zumindest dass es Absicht ist, und nicht einfach nur noch unvollständiger Code.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Funktions- und Methodennamen beschreiben üblicherweise die Tätigkeit die sie durchführen, damit der Leser das weiss und um sie besser von eher passiven Werten unterscheiden zu können. `aufgabe()` ist keine Tätigkeit.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. `d` für ein Wörterbuch geht gar nicht, und das `l` bei `l_eintrag` auch nicht.

Grundatentypen haben in Namen nichts zu suchen. So supergenerisch wie `liste` ist das noch mal unschöner. Der Leser will normalerweise nicht wissen was für Datentyp das ist, sondern was der Wert bedeutet im Programm bedeutet. `liste` wäre Beispielsweise `zeilen`.

Wobei das unüblich ist `readlines()` zu verwenden wenn man nicht tatsächlich eine Liste mit *allen* Zeilen braucht. Da die eine nach der anderen verarbeitet werden, braucht man die nicht alle auf einmal im Speicher und man kann einfach über das Datei-Objekt iterieren. Das liefert die Zeilen ohne sie erst komplett in den Speicher zu lesen.

Dateien öffnet man wo es geht zusammen mit der ``with``-Anweisung. Bei Textdateien sollte man immer die Kodierung mit angeben, sonst wird ”geraten” und das kann dann auch falsch sein.

Das mit den Leerzeichen als Trenner geht übrigens nicht weil es Begriffe gibt, die aus mehreren Worten bestehen, die durch Leerzeichen getrennt sind. Im deutschen seltener weil wir Worte dann oft einfach zusammenschreiben oder mit einem Bindestrich verbinden, aber im Englischen beispielsweise ist dann da einfach ein Leerzeichen. Beispiel „Programmiersprache“ → „programming language“.

Das eine Funktion, die eine Aufgabe aus einem Wörterbuch stellt, dieses auch verändert, ist überraschend. Das sollte klar dokumentiert werden.

Die Anzahl der Einträge in einem Wörterbuch ist immer gleich der Anzahl der Schlüssel. Darum macht es keinen Sinn sich erst die Schlüssel zu holen um dann *deren* Anzahl mit `len()` zu bestimmen.

Wenn man statt `randint()` die Funktion `randrange()` verwendet, kann man sich das abziehen der 1 von der Anzahl sparen. Aber selbst `randrange()` ist noch unnötig umständlich, weil das `random`-Modul auch eine Funktion hat, die ein zufälliges Element aus einer Sequenz auswählt. Womit dann auch `zufall` wegfällt, was vielleicht auch besser `index` oder `zufallsindex` gehiessen hätte.

Da man neben der Vokabel auch garantiert die Übersetzungen braucht, macht es Sinn sich gleich beides zufällig auszuwählen und jeweils an einen Namen zu binden.

Werte und Zeichenkettenteile mit ``+`` zusammensetzen ist eher BASIC denn Python und wird schnell unübersichtlich. In Python gibt es Zeichenkettenformatierung mit der `format()`-Methode oder f-Zeichenkettenliteralen.

Ein `print()` mit ``end=""`` und gleich danach ein ``print()`` ist ein bisschen sinnlos. Warum erst das Zeilenende unterdrücken um gleich danach ein `print()` zu haben das ein Zeilenende ausgibt‽

Da würde man auch eher nur mit *einem* `print()` arbeiten, und die Übersetzungen mit `join()` vorher zusammensetzen.

Das `input()` am Ende ist sinnlos bis nervig und sollte da nicht stehen.

Ungetestet

Code: Alles auswählen

#!/usr/bin/env python3
import random


def woerterbuch_laden(pfad):
    with open(pfad, encoding="utf-8") as datei:
        woerterbuch = {}
        for zeile in datei:
            #
            # BUG Das geht so nicht Leerzeichen als Trenner zu verwenden, weil
            #   Leerzeichen *in* Vokabeln und einzelnen Übersetzungen vorkommen
            #   können!
            #
            wort, *uebersetzungen = zeile.split()
            woerterbuch[wort] = uebersetzungen

    return woerterbuch


def aufgabe_stellen(woerterbuch):
    """
    Fragt eine zufällig gewählte Vokabel aus dem `woerterbuch` ab.

    ACHTUNG: Bei einer richtigen Antwort wird der Eintrag aus dem `woerterbuch`
             entfernt!
    """
    vokabel, uebersetzungen = random.choice(list(woerterbuch.items()))
    antwort = input(f"Wie lautet das deutsche Wort für {vokabel}?\n")
    if antwort in uebersetzungen:
        print("Richtig!")
        del woerterbuch[vokabel]
    else:
        print("Leider falsch.")
        print(vokabel, "bedeutet:", "\n".join(uebersetzungen))


def main():
    print("Vokabeltrainer")
    print()
    woerterbuch = woerterbuch_laden(
        R"C:\Users\Birgit\source\repos\PythonDictonary\PythonDictonary\get.txt"
    )
    while woerterbuch:
        aufgabe_stellen(woerterbuch)

    print("Sie haben alle Vokabeln gelernt.")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten