Schleifen "Türsteher" akzeptiert meine Eingabe nicht?

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
rich96
User
Beiträge: 5
Registriert: Donnerstag 28. Mai 2020, 19:52

Hallöchen,

ich bin Rich (nicht reich ;)) ich möchte mir selbst Python beibringen und hab heute (nachdem ich mal alle Grundlagen "studiert" hatte) mit einem kleinem Rechner angefangen. Wie es so immer ist, kamen mir immer weiter neue Ideen und nun komm ich nicht weiter. Meine Vermutung legt nahe, dass es bei der Typen Abfrage hackelt :/

Momentan ist die Ausgabe wie folt:
Man soll eine Eingabe für die Variabeln a b c eingeben. EGAL was ich eingebe, es folgt ein "Fehler" und die Eingabe beginnt von vorn. Dabei soll die Eingabe nur dann von vorne beginnen wenn die Eingabe keine Zahl ist. Sprich, wenn sie leer ist oder einen string aufweist.

Vielleicht findet sich hier jemand der mir einen Tipp geben kann woran es scheitert :)

Zudem erhalte ich bei der ersten While Schleife in der Abfrage in der Zeile " sa = input("Seitenlänge a: ")" einen Fehler angezeigt:
[mccabe] Cyclomatic complexity too high: 15 (threshold 15)

Laut einer online Recherche liegt es an zu vielen "ifs" aber das kann doch nicht sein, oder?

Für jede Hilfe wäre ich dankbar :)

LG Rich96

Code: Alles auswählen

print("Rechner")

sa = ""
sb = ""
sc = "" 
le = "Leere Eingabe"

while sa == "" or sb == "" or sc == "":
  sa = input("Seitenlänge a: ")
  sb = input("Seitenlänge b: ")
  sc = input("Seitenlänge c: ")
  
  if type(sa) != str and type(sb) != str and type(sc) != str:
    a = float(sa)
    b = float(sb)
    c = float(sc)

    print("")
    print("Gültige Angabe: a: {0}, b: {1}, c:{2}  ". format(a,b,c))

    multierg = a * b * c
    adderg = a + b + c
    suberg = a - b - c
    if not a == 0 or not b == 0 or not c ==0:
      diverg = a / b / c
    else:
      diverg ="'division durch 0 nicht möglich'"

    print("")
    print("Welche Rechnung möchstest Du durchführen?")
    print("")
    print("Drücke 1 für Multiplikation")
    print("Drücke 2 für eine Addition")
    print("Drücke 3 für eine Subtraktion")
    print("Drücke 4 für eine Division")

    try:
      eingabe = -1
      while not eingabe == 1 or not eingabe == 2 or not eingabe == 3 or not eingabe == 4 or not eingabe == 0:
        eingabe = int(input())
        if eingabe == 1:
          print("Das Ergebnis lautet {0}". format(multierg))
          print("Um Rechner zu beenden drück '0'")
        elif eingabe == 2:
          print("Das Ergebnis lautet {0}". format(adderg))
          print("Um Rechner zu beenden drück '0'")
        elif eingabe == 3:
          print("Das Ergebnis lautet {0}". format(suberg))
          print("Um Rechner zu beenden drück '0'")
        elif eingabe == 4:
          print("Das Ergebnis lautet {0}". format(diverg))
          print("Um Rechner zu beenden drück '0'")
        elif eingabe == 0:
          print("Rechner verlassen.")
          break
        else:
          print("Fehlerhafte Eingabe, neuer Versuch")
    except:
      ("except")

  elif sa == "":
    print(le)
    continue
  elif sb == "":
    print(le)
    continue
  elif sc == "":
    print(le)
    continue
  else:
    sa = ""
    sb = ""
    sc = ""
    print("Fehler")
    continue


Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Da liegt einiges im Argen.

Du solltest dein Programm nicht am Stück schreiben, sondern Stück für Stück. Und diese Stücke immer testen. Am besten, indem du die Stücke in Funktionen kapselst.

Und prüfe doch mal einfach nach der Eingabe, von welchem Typ deine Eingaben sind. Input liefert _immer_ eine Zeichenkette. Und die musst du selbst umwandeln.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 2. Keine Abkürzungen. Was für ein Tier soll dieses multier sein? Keine nakten except, weil das jede Fehlersuche unmöglich macht. Kein `continue`, hat hier auch nirgends einen Effekt.
while-Schleifen, die nur starten können, weil man den Variablen einen Dummy-Wert zuweisen muß, sind keine richtigen while-Schleifen, sondern while-True, wo die Abbruchbedingung am Ende per `break`stehen sollte. Aber die while-Schleifen, die Du so schreibst, sind eh seltsam.
Statt `not a == b` gibt es `a != b`.
Wie sparrow schon geschrieben hat, schreibt man nicht 75 Zeilen Code, ohne auch nur eine davon getestet zu haben. Ein Programm besteht aus vielen kleinen Teile, die man alle einzeln Testet, weil jeder Teil eine bestimmte Funktion hat, und deshalb schreibt man das auch für diese Teile auch Funktionen.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ergänzend: Typprüfungen im Code sind ein „code smell“. Wenn man das macht, dann macht man in der Regel etwas falsch. Und falls man doch mal einen der wenigen Fälle hat, in denen eine Typprüfung sinnvoll ist, dann macht man das nicht mit `type()` und ``==`` sondern mit `isinstance()`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
rich96
User
Beiträge: 5
Registriert: Donnerstag 28. Mai 2020, 19:52

Ok,
ich hab den Code jetzt in zwei Funktionen aufgeteilt.
Problem 1: Wie kann ich isinstance mit einer if abfrage kombinieren?
Problem 2: Wie rufe ich in einer Funktion, die Variablen aus einer vorherigen Funktion ab?

Funktion eingabe() funktioniert soweit,
Funktion berechnung_ausgabe() zickt rum, da sie nicht weiß, woher sie variable_a und variable_b her nehmen soll.

Code: Alles auswählen

def eingabe():
    a = input("Dein erster Wert: ")
    b = input("Dein zweiter Wert: ")
    c = input("Dein dritter Wert: ")

    try:
        variable_a = float(a)
        variable_b = float(b)
        variable_c = float(c)
    except ValueError:
            print("Fehlerhafte Eingabe")
    
    return variable_a, variable_b, variable_c

def berechnung_auswahl():
    print("Welche Berechnung soll ausgeführt werden?")
    print("Drücke 1 für Multiplikation \n  Drücke 2 für Addition \n Drücke 3 für Subtraktion  \n Drücke 4 für Division")
    try:
        auswahl = -1
        while auswahl != 1 or auswahl != 2 or auswahl != 3 or auswahl != 4 or auswahl != 0:
            auswahl = int(input())
            if auswahl == 1:
                multiplikation_erg = eingabe.variable_a * eingabe.variable_b * eingabe.variable_c
                print("Das Ergebnis lautet {0}". format(multipikation_erg))
                print("Um Rechner zu beenden drücke 0")
            elif auswahl == 2:
                addition_erg = variable_a + variable_b + variable_c
                print("Das Ergebnis lautet {0}". format(addition_erg))
                print("Umrechner zu beenden drücke 0")
            elif auswahl == 3:
                subtraktion_erg = variable_a - variable_b - variable_c
                print("Das Ergebnis lautet {0}". format(subtraktion_erg))
                print("Um Rechner zu beenden drücke 0")
            elif auswahl == 4:
                print("Das Ergebnis lautet {0}". format(division_erg))
                print("Um Rechner zu beenden drücke 0")
            elif auswahl == 0:
                print("Rechner verlassen")
                break
            else:
                print("Du darfst nur Werte zw. 0 und 4 eingeben. ")
    except ValueError:
        print("String detected")
    return auswahl


Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rich96: `input()` ist eine Funktion die eine Zeichenkette zurück gibt. `int()` ist eine Funktion die eine ganze Zahl zurück gibt. Schau doch mal wie Du die Aufrufst und was Du mit dem Rückgabewert machst. Das musst Du mit `eingabe()` halt genau so machen. Die hat noch die Besonderheit, das der eine Wert den sie zurück gibt, ein Tupel mit drei Zahlen ist. Das heisst da willst Du wahrscheinlich nicht das Tupel direkt verwenden, sondern den Inhalt an drei Namen binden die dann in der `berechnung_auswahl()`-Funktion verwendet wird.

Die Ausnahmebehandlung in `eingabe()` ist fehlerhaft. Wenn da eine Umwandlung in eine Gleitkommazahl nicht klappt, wird zwar der entsprechende Text ausgegeben, aber dann wird versucht das Ergebnis von allen drei Umwandlungen an den Aufrufer der Funktion zurück zu geben. Nur sind die Namen ja gar nicht alle definiert falls eine Umwandlung nicht geklappt hat und das führt dann zwangsläufig zu einem `NameError`.

Die ``while``-Schleife hat immer noch die sinnfreie und zudem noch sehr kompliziert ausgedrückte Bedingung. Die Definition von `auswahl` vor der Schleife muss weg und die Bedingung ist einfach `True`.

Bei den Namen bist Du dem Hinweis mit den Abkürzungen nur halb gefolgt. Es ist gut das `multiplikation` jetzt ausgeschrieben ist, aber `erg` sollte wohl `ergebnis` heissen. Und letztlich ist die Frage ob das wichtig ist wovon das ein Ergebnis ist, denn wenn es in allen vier ``if`-Zweigen nur `ergebnis` heissen würde, ohne einen Zusatz davor, dann sind die beiden `print()`-Zeilen in allen vier ``if``-Zweigen genau gleich. Und dann kann und sollte man den Code so strukturieren, dass man die nicht genau gleich vier mal hinschreiben muss. Das macht Programme nämlich schwerer zu warten und damit fehleranffälliger, weil man dann immer alle Kopien verändern muss wenn sich etwas ändert oder man einen Fehler in den Kopien beheben muss. Man darf dabei keine Kopie vergessen und muss auch alle auf die gleiche Weise ändern. Einen Fehler gibt es da ja bereits der nur in einer der Kopien existiert: Bei der Addition wird „Umrechner zu beenden […]“ ausgegeben, statt „Um Rechner zu beenden […]“ wie bei den anderen Rechenarten.

Um eine Regelmässige Struktur zu erhalten muss man die beiden Sonderfälle „Eingabe von 0“ und „Eingabe von ungültiger Auswahl“ anders behandeln. Bei der 0 bietet es sich an die sofort als erstes nach der Eingabe zu behandeln. Dass das ``break`` so weit unten in der Schleife ”versteckt” ist, finde ich persönlich sowieso nicht so gut.

Den Sonderfall das der Benutzer etwas anderes als 0 bis 4 eingegeben hat, könnte man explizit testen.

Die Eingabe von drei Zahlen ist für alle Rechenoperationen gleich, das sollte also auch nur *einmal* im Code stehen und nicht für jede Rechenoperation einzeln. Also vor dem ``if``/``elif``-Konstrukt wo dann nur ein `ergebnis` berechnet wird. Die Ausgabe davon steht dann dahinter.

Warum die `auswahl` am Ende an den Aufrufer der Funktion zurückgegeben wird ist mir nicht klar. Zumal es auch passieren kann, dass zu dem Zeitpunkt der Name `auswahl` gar nicht definiert ist und es zu einem `UnboundLocalError` kommt.

Ungetestet:

Code: Alles auswählen

def berechnung_auswahl():
    print("Welche Berechnung soll ausgeführt werden?")
    print(
        "Drücke 1 für Multiplikation\n"
        "Drücke 2 für Addition\n"
        "Drücke 3 für Subtraktion\n"
        "Drücke 4 für Division"
    )
    try:
        while True:
            auswahl = int(input())
            if auswahl == 0:
                print("Rechner verlassen")
                break
            
            if 1 <= auswahl <= 4:
                ...  # Hier kommt die Abfrage der drei Zahlen hin.

                if auswahl == 1:
                    ergebnis = variable_a * variable_b * variable_c
                elif auswahl == 2:
                    ergebnis = variable_a + variable_b + variable_c
                elif auswahl == 3:
                    ergebnis = variable_a - variable_b - variable_c
                elif auswahl == 4:
                    ergebnis = variable_a / variable_b / variable_b
                else:
                    assert False, "unerwartete Auswahl {!r}".format(auswahl)

                print("Das Ergebnis lautet {0}".format(ergebnis))
                print("Um Rechner zu beenden drücke 0")
            else:
                print("Du darfst nur Werte zw. 0 und 4 eingeben. ")
    except ValueError:
        print("String detected")
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
rich96
User
Beiträge: 5
Registriert: Donnerstag 28. Mai 2020, 19:52

Achso, jetzt verstehe ich was ihr mit True in der Schleife meintet.
Stimmt, dass es im Grunde garnicht 4 verschiedene Ergebnisse braucht hatte ich außer acht gelassen, ist ja logisch.

Danke für die Antworten und dem Quellcode.
Ich habe meine "seltsame" Methode der while Schleife aus einem Grundlagen Lehrvideo aus udemy.com geholt. Hab mir grad die Dokumentation von Python angeschaut, und da wird das so erklärt wie ihr geschrieben hattet. Dann schaufel ich mich mal durch die doku durch mit der Macht von Google Translate x)
Antworten