Mein erstes Programm (Vokabeltrainer)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Jadefalke
User
Beiträge: 2
Registriert: Sonntag 9. September 2018, 09:03

Hallo zusammen!
Ich versuche nun schon seit ein paar Jahren mir das Programmieren beizubringen.
Meine bisherigen erfahrungen sind:
Ruby, gefiel mir bisher immer noch am besten
Lua, hauptsächlich in verbindung mit Minecraft
und zum Schluß C++ sieht toll aus macht auch Spaß aber definitiv zu komplex für mich als einsteiger.
Mein Bruder sagte mir dann probier mal Python.
Bisher bin ich total begeistert.
Python ist genauso hübsch wie Ruby hat aber eine deutlich größere Fangemeinde und mindestens so gut wie C++ aus meiner Sicht ;-)
Heute habe ich mein erstes Programm fertig und würde gerne mal eure Meinung und verbesserungsvorschläge hören.

Code: Alles auswählen

import random
import subprocess
import pickle
import shelve


Schwierigkeitsgrad = ("Rost", "Eisen", "Stahl", "Carbon", "Titan", "Adamantium")
Level = 0


def cs():
    if subprocess.call('cls', shell=True) > 0:
        subprocess.call('clear', shell=True)


def Startmenue():
	cs()
	print("Willkommen bei deinem persönlichen Vokabeltrainer")
	print("Dies ist das Startmenue von hier hast Du mehrere Möglichkeiten")
	print("(1) Neue Vokabeln eingeben")
	print("(2) Schwierigkeitsgrad wählen")
	print("(3) Vokabeln lernen")
	print("(4) Trainer verlassen")
	wahl = int(input("Triff deine Wahl: "))
	if wahl == 1:
		eingabe()
	elif wahl == 2:
		Wahl()
	elif wahl == 3:
		Lernen()
	else:
		print("Auf Wiedersehen!")

def Lernen():
	cs()
	v = int(input("Wieviele Vokabeln möchtest Du lernen? "))
	i = 1
	
	Fach = shelve.open(Schwierigkeitsgrad[Level])
	while i <= v:
		cs()
		wort = random.choice(list(Fach.keys()))
		#wort = random.choice(list(Vokabeln.values()))
		print("Hier ist deine Vokabel:\n")
		print(wort.center(25))
		print("\n")
		antwort = input("Wie lautet deine Antwort?\n")
		#print(random.choice(list(Vokabeln.keys())))
		if antwort == Fach[wort]:
			print("Richtig")
			if Level == 5:
				print("Wow fantastisch diese Vokabel kannst du nun perfekt!")
				del Fach[wort]
			else:
				print("Sehr gut die Vokabel steigt ein Level auf")
				print("Das neue Level ist jetzt: " + Schwierigkeitsgrad[Level+1])
				a = wort
				b = Fach[wort]
				c = shelve.open(Schwierigkeitsgrad[Level+1])
				c[a] = b
				c.close()
				del Fach[wort]
			input("Weiter mit Enter")
		else:
			print("Falsch")
			print("%s bedeutet übersetzt: %s" % (wort, Fach[wort]))
			if Level == 0:
				print("Beim nächsten Mal schaffst du es garantiert!")
			else:
				print("Oh Nein die Vokabel steigt ein Level ab")
				print("Das neue Level ist jetzt: " + Schwierigkeitsgrad[Level-1])
				a = wort
				b = Fach[wort]
				c = shelve.open(Schwierigkeitsgrad[Level-1])
				c[a] = b
				c.close()
				del Fach[wort]
			input("Weiter mit Enter")
		i += 1
	Fach.close()
	Startmenue()

def Wahl():
	cs()
	global Level
	print("Wähle deinen Schwierigkeitsgrad")
	print("(1) Rost (Hier findest Du neue Vokabeln und alles was du noch nicht kannst)")
	print("(2) Eisen (Diese Vokablen hattest Du schon einmal richtig)")
	print("(3) Stahl")
	print("(4) Carbon")
	print("(5) Titan")
	print("(6) Adamantium (Alles was hier ist kannst Du schon echt gut")
	Level = int(input("Welches Level hättest Du denn gerne? "))-1
	# if sg == 1:
		# Schwierigkeitsgrad = "Rost"
	# elif sg == 2:
		# Schwierigkeitsgrad = "Eisen"
	# elif sg == 3:
		# Schwierigkeitsgrad = "Stahl"
	# elif sg == 4:
		# Schwierigkeitsgrad = "Carbon"
	# elif sg == 5:
		# Schwierigkeitsgrad = "Titan"
	# elif sg == 6:
		# Schwierigkeitsgrad = "Adamantium"
	Startmenue()

def eingabe():
	cs()
	regal = shelve.open("Rost")
	i = 1
	#print(regal)
	t = int(input("Wieviel Vokabeln möchtest Du eingeben? "))
	
	while i <= t:
		a = input("Deutsches Wort: ")
		b = input("Englisches Wort: ")
		regal[a] = b
		i +=1
	#print (dict(regal))
	#input("Warte kurz")
	regal.close()
	Startmenue()



Startmenue()
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jadefalke: Der Programmfluss ist fehlerhaft. Du ”löst” das komplett über Rekursion was nur eine bestimmte Zeit lang gut geht, bis das Programm dann mit einer Fehlermeldung aussteigt, wenn das Rekursionslimit erreicht ist. Funktionen sind keine Sprungmarken zu denen man per Aufruf hinspringt, sondern die ruft man auf, dann tun die etwas, *und kehren zum Aufrufer zurück*. Das ist auch in Ruby und C++ so. In Lua kommt man damit durch weil die Sprache eine garantierte „Tail Call Optimisation“ (TCO) hat. Wenn Du etwas wiederholen möchtest, nimm eine Schleife.

``global`` hat in einem sauberen Programm nix zu suchen. Alles was eine Funktion braucht (ausser Konstanten), sollte als Argument(e) in die Funktion kommen und wenn die Funktion ein Ergebnis hat, dann gibt man das als Rückgabewert zum Aufrufer zurück und ändert nicht eine globale Variable.

`pickle` wird importiert, aber nirgends verwendet.

Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_IN_GROSSBUCHSTABEN) und Klassen (MixedCase).

Tupel werden üblicherweise für Werte benutzt wo die einzelnen Elemente unterschiedliche Bedeutungen haben. Wenn alle die gleiche Bedeutung haben, nimmt man eher eine Liste. Das betrifft den `Schwierigkeitsgrad`.

Namen sollten aussagekräftig sein und nicht irgendwelche kryptischen Abkürzungen (`cs`), und Funktions- und Methodennamen beschreiben üblicherweise die Tätigkeit die sie Ausführen. `Startmenu` ist zum Beispiel keine Tätigkeit. Das wäre eher ein Name für eine Datenstruktur die ein Startmenu beschreibt, bzw. wegen dem grossen S würde ein Pythonprogrammierer hier eine Klasse erwarten.

Wenn man weiss wie oft etwas wiederholt werden soll, nimmt eine ``for``-Schleife und keine ``while``-Schleife.

Dateien öffnet man am besten zusammen mit der ``with``-Anweisung um sicherzustellen, dass sie am Ende auch ganz bestimmt wieder geschlossen werden, egal aus welchem Grund/wie der Block verlassen wird. Auch `shelve`-Objekte sind Kontextmanager die man mit ``with`` verwenden kann. Bei Objekten die eine `close()`-Methode haben, aber selbst keine Kontextmanager sind, hilft `contextlib.closing()` weiter.

Wobei die Eingabe besser wäre wenn man beliebig viele Vokabeln eingeben kann und den Vorgang zum Beispiel mit einer Leereingabe abbrechen kann.

`Wahl()` sagt so überhaupt nicht aus *was* da gewählt wird.

Vermeide magische Zahlen, wie die 5 in `Lernen()`. Der Leser weiss da nicht sofort was die Zahl bedeutet. Meistens definiert man für so etwas eine Konstante, aber in diesem Fall ist die Zahl ja zusätzlich von der Anzahl der Schwierigkeitsgrade abhängig. Wenn man die ändert, muss man auch diese Zahl anpassen. Sicherer ist es hier die `len()`-Funktion auf die Liste mit den Schwierigkeitsgraden anzuwenden, denn dann ändert sich der Wert automatisch wenn man diese Liste ändert.

An der Stelle scheint mir ein Fehler im Code zu sein, denn wenn die Vokabel in der vorletzten Stufe ist, wird sie nicht in die letzte kopiert.

Das verschieben einer Vokabel in ein anderes Shelve ist zwei mal auskodiert. Das würde man in eine Funktion heraus ziehen.

Validierung der Eingaben fehlt bei den ganzen Benutzereingaben. Man glaubt gar nicht was Benutzer so eingeben, selbst wenn die Anweisungen kristallklar sind.

Ungetestet:

Code: Alles auswählen

import random
import shelve
import subprocess


SCHWIERIGKEITSGRADE = [
    'Rost', 'Eisen', 'Stahl', 'Carbon', 'Titan', 'Adamantium'
]


def clear_screen():
    if subprocess.call('cls', shell=True) > 0:
        subprocess.call('clear', shell=True)


def wort_verschieben(wort, fach, level, level_delta):
    ziel_level = level + level_delta

    if level == ziel_level:
        raise ValueError('ziel darf nicht die quelle sein')
    if not 0 <= ziel_level < len(SCHWIERIGKEITSGRADE) - 1:
        raise ValueError('ungültiges Ziel')

    with shelve.open(SCHWIERIGKEITSGRADE[ziel_level]) as ziel_fach:
        ziel_fach[wort] = fach[wort]
    del fach[wort]


def vokabeln_eingeben():
    clear_screen()
    with shelve.open('Rost') as deutsch_nach_englisch:
        for _ in range(int(input('Wieviel Vokabeln möchtest Du eingeben? '))):
            deutsches_wort = input('Deutsches Wort: ')
            englisches_wort = input('Englisches Wort: ')
            deutsch_nach_englisch[deutsches_wort] = englisches_wort


def waehle_schwierigkeitsgrad():
    clear_screen()
    print('Wähle deinen Schwierigkeitsgrad')
    print(
        '(1) Rost (Hier findest Du neue Vokabeln und alles was du noch nicht'
        ' kannst)'
    )
    print('(2) Eisen (Diese Vokablen hattest Du schon einmal richtig)')
    print('(3) Stahl')
    print('(4) Carbon')
    print('(5) Titan')
    print('(6) Adamantium (Alles was hier ist kannst Du schon echt gut')
    return int(input('Welches Level hättest Du denn gerne? ')) - 1


def vokabeln_abfragen(level):
    with shelve.open(SCHWIERIGKEITSGRADE[level]) as fach:
        clear_screen()
        for _ in range(int(input('Wieviele Vokabeln möchtest Du lernen? '))):
            clear_screen()
            wort = random.choice(list(fach))
            print('Hier ist deine Vokabel:\n')
            print(wort.center(25))
            print('\n')
            antwort = input('Wie lautet deine Antwort?\n')
            if antwort == fach[wort]:
                print('Richtig')
                if level == len(SCHWIERIGKEITSGRADE) - 1:
                    print(
                        'Wow fantastisch diese Vokabel kannst du nun perfekt!'
                    )
                    del fach[wort]
                    # 
                    # TODO Müsste die nicht aus der vorletzten in die letzte
                    #   Datei kopiert werden?
                    # 
                else:
                    print('Sehr gut die Vokabel steigt ein Level auf')
                    print(
                        'Das neue Level ist jetzt: {}'.format(
                            SCHWIERIGKEITSGRADE[level + 1]
                        )
                    )
                    wort_verschieben(wort, fach, level, 1)
                input('Weiter mit Enter')
            else:
                print('Falsch')
                print('{} bedeutet übersetzt: {}'.format(wort, fach[wort]))
                if level == 0:
                    print('Beim nächsten Mal schaffst du es garantiert!')
                else:
                    print('Oh Nein die Vokabel steigt ein Level ab')
                    print(
                        'Das neue Level ist jetzt: {}'.format(
                            SCHWIERIGKEITSGRADE[level-1]
                        )
                    )
                    wort_verschieben(wort, fach, level, -1)
                input('Weiter mit Enter')


def main():
    level = 0
    
    while True:
        clear_screen()
        print('Willkommen bei deinem persönlichen Vokabeltrainer')
        print('Dies ist das Startmenue von hier hast Du mehrere Möglichkeiten')
        print('(1) Neue Vokabeln eingeben')
        print('(2) Schwierigkeitsgrad wählen')
        print('(3) Vokabeln lernen')
        print('(4) Trainer verlassen')
        wahl = int(input('Triff deine Wahl: '))
        if wahl == 1:
            vokabeln_eingeben()
        elif wahl == 2:
            level = waehle_schwierigkeitsgrad()
        elif wahl == 3:
            vokabeln_abfragen(level)
        else:
            print('Auf Wiedersehen!')
            break


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Jadefalke
User
Beiträge: 2
Registriert: Sonntag 9. September 2018, 09:03

Oh wow danke.
Ich sehe schon, ich habe noch eine Menge zu lernen.
Ich fand mich eigentlich ganz gut bei meinen ersten Schritten ;-)
Pickle hatte ich in einer früheren Version verwendet aber Shelve fühlt sich für mich einfacher an. Aus Unachtsamkeit habe ich es noch im Code stehen :-(
Ja der Fehler mit dem Kopieren der Vokabel in das letzte Fach ist mir selbst noch gar nicht aufgefallen.
Da habe ich mich einfach verzählt.
Ich werde jetzt mal alles was du geschrieben hast durcharbeiten und noch einige Begriffe nachschlagen.
Danach werde ich mich noch mal ransetzen ;-)
Antworten