Python Anfänger Problem if..or..or

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
eface
User
Beiträge: 4
Registriert: Montag 12. August 2019, 14:47

Hallo miteinander,

ich probiere mich momentan etwas an Python und verstehe gerade das Problem nicht, was Python mit meinerm Code macht.
Ich habe 6 Auswahlmöglichkeiten und noch eine Möglichkeit um das Programm zu beenden.

Bei 1-6 sollte er mir ok ausgeben. Das funktioniert auch soweit aber bei q sollte er mir das Programm beenden bzw den string "Programm beendet" ausgeben. Und bei allen anderen strings sollte das Programm neu starten.

Jedoch ist es so, dass scheinbar bei jeglicher Eingabe die erste Bedingung erfüllt wird (Ok wird ausgegeben) und ich kann mir nicht erklären warum.

Vielen Dank schon mal im Voraus für die Hilfe :)

Code: Alles auswählen

    
def Art_der_Umrechnung():
    print(" (1) Umrechnung von Celsius nach Kelvin \n (2) Umrechnung von Celsius nach Fahrenheit \n (3) Umrechnung von Kelvin nach Celsius \n (4) Umrechnung von Kelvin nach Fahrenheit \n (5) Umrechnung von Fahrenheit nach Celsius \n (6) Umrechnung von Fahrenheit nach Kelvin \n (q) Programm beenden \n")
    global wahl
    wahl=input("Bitte wählen ")
    if wahl == "1" or "2" or"3" or "4" or "5" or "6":
        print("ok")
    elif wahl == "q":
        print("Programm beendet")
    else:
        Art_der_Umrechnung()
        
Art_der_Umrechnung()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

"==" bindet stärker als "or". Und alles was nach dem ersten "or" kommt sind Strings der Länge 1 und damit True. Somit erhältst Du immer "ok".
eface
User
Beiträge: 4
Registriert: Montag 12. August 2019, 14:47

Dank für die schnelle Antwort.

Inwiefern ist es denn dabei relevant, dass die strings eine länge von 1 haben?

D.h. wenn ich es wenn ich es mit if wahl == machen wollte, müsste ich mit fünf mal elif arbeiten ?

Code: Alles auswählen


if wahl == "1"
	...
elif wahl =="2"
	...
usw.
habe es aber nun mit folgender, deutlich eleganterer Lösung gemacht:

Code: Alles auswählen

if wahl in("1","2" , "3" , "4" , "5" , "6"):
	print("ok")
__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Naja. Die Variante tut ja nix. Irgendwann muss ja abhängig von der Zahl was unterschiedliches passieren. Da bist du wieder beim elif.
eface
User
Beiträge: 4
Registriert: Montag 12. August 2019, 14:47

Also das war nur die gekürzte Variante, vollständig sah das so aus und macht auch mehr sinn:

Code: Alles auswählen

def Art_der_Umrechnung():
    print(" (1) Umrechnung von Celsius nach Kelvin \n (2) Umrechnung von Celsius nach Fahrenheit \n (3) Umrechnung von Kelvin nach Celsius \n (4) Umrechnung von Kelvin nach Fahrenheit \n (5) Umrechnung von Fahrenheit nach Celsius \n (6) Umrechnung von Fahrenheit nach Kelvin \n (q) Programm beenden \n")
    global wahl
    wahl=input("Bitte wählen ")
    if wahl in("1","2" , "3" , "4" , "5" , "6"):
        get_temp()
    elif wahl == "q":
        print("Programm beendet")
    else:
        Art_der_Umrechnung()
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Macht nicht mehr Sinn, denn Du solltest keine globalen Variablen benutzen und warum Du ein get_temp aufrufst, obwohl da gar nichts geholt wird, ist auch seltsam.
Rekursion ist eine schlechte Art eine Schleife auszudrücken. Funktionen sollten eine Tätigkeit ausdrücken, 'Art_der_Umrechnung' ist keine. Statt in einem Print mehrere Zeilen auszugeben ist es lesbarer, das in mehreren Prints zu machen.

Besser wäre also:

Code: Alles auswählen

def art_der_umrechnung_auswaehlen_und_gleich_umrechnen():
    while True:
        print(" (1) Umrechnung von Celsius nach Kelvin")
        print(" (2) Umrechnung von Celsius nach Fahrenheit")
        print(" (3) Umrechnung von Kelvin nach Celsius")
        print(" (4) Umrechnung von Kelvin nach Fahrenheit")
        print(" (5) Umrechnung von Fahrenheit nach Celsius")
        print(" (6) Umrechnung von Fahrenheit nach Kelvin")
        print(" (q) Programm beenden \n")
        wahl = input("Bitte wählen ")
        if wahl in("1","2" , "3" , "4" , "5" , "6"):
            get_temp(wahl) # TODO: besserer Name
            break
        elif wahl == "q":
            print("Programm beendet")
            break
Du merkst an dem Namen, dass die Funktion eigentlich zu viel tut. Wenn das das ganze Programm ist, könnte man es einfach "main" nennen, sonst wäre die Auswahl und das Umrechnen besser in zwei getrennten Funktionen:

Code: Alles auswählen

def art_der_umrechnung_auswaehlen():
    while True:
        print(" (1) Umrechnung von Celsius nach Kelvin")
        print(" (2) Umrechnung von Celsius nach Fahrenheit")
        print(" (3) Umrechnung von Kelvin nach Celsius")
        print(" (4) Umrechnung von Kelvin nach Fahrenheit")
        print(" (5) Umrechnung von Fahrenheit nach Celsius")
        print(" (6) Umrechnung von Fahrenheit nach Kelvin")
        print(" (q) Programm beenden \n")
        wahl = input("Bitte wählen ")
        if wahl in("q", "1","2" , "3" , "4" , "5" , "6"):
            return wahl

def main():
    wahl = art_der_umrechnung_auswaehlen()
    if wahl == "q":
        print("Programm beendet")
    else:
        get_temp(wahl) # TODO: besserer Name

if __name__ == '__main__':
    main()
eface
User
Beiträge: 4
Registriert: Montag 12. August 2019, 14:47

Vielen Dank, das hat mir schon mal sehr viel weiter geholfen. Insbesonders beim Verständnis bzw. der Verwendung der while Schleife, aber auch wie ich die globale Variable vermeiden kann

Die Benennungen habe ich nun eindeutig nach der Verwendung Funktion gemacht und auf das global wahl konnte ich nun auch verzichten.

Ich hatte vorher nicht den gesamten Quellcode hier eingestellt. Deswegen kam es wohl zu Verwirrungen.

Hier also nochmal der komplette Code:

Code: Alles auswählen

def Art_der_Umrechnung_auswaehlen():
    print(" (1) Umrechnung von Celsius nach Kelvin")
    print(" (2) Umrechnung von Celsius nach Fahrenheit")
    print(" (3) Umrechnung von Kelvin nach Celsius")
    print(" (4) Umrechnung von Kelvin nach Fahrenheit") 
    print(" (5) Umrechnung von Fahrenheit nach Celsius")
    print(" (6) Umrechnung von Fahrenheit nach Kelvin")
    print(" (q) Programm beenden \n")
    while True:
        wahl=input("Bitte wählen ")
        if wahl == ("1"):
            C_to_K()
            break
        elif wahl == ("2"):
            C_to_F()
            break
        elif wahl == ("3"):
            K_to_C()
            break
        elif wahl == ("4"):
            K_to_F()
            break
        elif wahl == ("5"):
            F_to_C()
            break
        elif wahl == ("6"):
            F_to_K()
            break
        elif wahl == "q":
            print("Programm beendet")
            break


def Temperatur_Eingabe():
    while True:
        # global X
        X = input("Temperatur angeben angeben ")
        try:
            X=float(X)
            return X
        except:
            ValueError
            print("keine zahl eingegeben")
    

def  C_to_K():
    X = Temperatur_Eingabe()
    while X < -273.15:
        print("T zu klein")
        get_temp()
        break
    
    C = X
    K = C + 273.15
    print("\n")
    print(str(C) + " °C entsprechen " + str(K) + " Kelvin")
    print("\n")
    Art_der_Umrechnung_auswaehlen()


def C_to_F():
    X = Temperatur_Eingabe()
    C = X
    F = 9/5 * C + 32
    print("\n")
    print(str(C) + " °C entsprechen " + str(F) + " ° Fahrenheit")
    print("\n")
    Art_der_Umrechnung_auswaehlen()

    
def K_to_C():
    X = Temperatur_Eingabe()
    K = X
    C = K - 273.15
    print("\n")
    print(str(K) + " Kelvin entsprechen " + str(C) + " °C")
    print("\n")
    Art_der_Umrechnung_auswaehlen()


def K_to_F():
    X = Temperatur_Eingabe()
    K = X
    F = (K - 273.15) * 9/5 + 32
    print("\n")
    print(str(K) + " Kelvin entsprechen " + str(F) + " ° Fahrenheit")
    print("\n")
    Art_der_Umrechnung_auswaehlen()

    
def F_to_C():
    X = Temperatur_Eingabe()
    F = X
    C = 5/9 * (F-32)
    print("\n")
    print(str(C) + " ° Fahrenheit entsprechen " + str(F) + " °Celsius")
    print("\n")
    Art_der_Umrechnung_auswaehlen()

    
def F_to_K():
    X = Temperatur_Eingabe()
    F = X
    K = 5/9 * (F-32) + 273.15
    print("\n")
    print(str(F) + " ° Fahrenheit entsprechen " + str(K) + " Kelvin")
    print("\n")
    Art_der_Umrechnung_auswaehlen()


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

@eface: Die wechselseitige Endlosrekursion ist falsch – das wird nämlich früher oder später mit einer Ausnahme abbrechen. Funktionen sollten etwas tun und dann zum Aufrufer zurückkehren, nicht immer wieder und weiter weitere Funktionen aufrufen und im Grunde nie zurückkehren. Rekursion ist in Programmiersprachen ohne garantierte Wegoptimierung von endrekursiven Aufrufen kein geeigneter Ersatz für eine ganz normale Schleife! Und selbst bei einer Sprache die das garantiert macht man das nicht *so*, weil man auf diese Weise keine Funktion mal einzeln testen kann.

Die Ausnahmebehandlung bei der Temperatureingabe ist falsch. Da werden *alle* Ausnahmen behandelt. Ich vermute Du willst da nur den `ValueError` behandeln, dann solltest Du das aber auch so schreiben das nur ein `ValueError` behandelt wird. Das `ValueError` in der ersten Zeile des ``except:``-Blocks hat keinen Effekt. Das muss wo anders stehen.

Mit dem `X` machst Du komische Sachen. Man muss Eingaben/Rückgaben von Funktionen nicht erst an einen Namen binden nur um gleich in der nächsten Zeile den Wert an den Namen zu binden der dann tatsächlich verwendet wird. Und auch Rückgabewerte muss man nicht zwingend an einen Namen binden. Das kann man mal machen wenn das ein guter, aussagekräftiger Name ist, um dem Leser beim Verständnis zu helfen, aber `X` ist kein Name der diese Bedingung erfüllt.

Bei den Vergleichen mit der `wahl` sind unnötige Klammern um die literalen Werte mit denen verglichen wird.

Zeichenketten und Werte mit `str()` und ``+`` zusammenstückeln ist eher BASIC denn Python. In Python gibt es dafür Zeichenkettenformatierung mit der `format()`-Methode auf Zeichenketten und ab Python 3.6 auch f-Zeichenkettenliterale. Bei den `print()`-Ausgaben hätte man auch einfach den Umstand nutzen können, das die Funktion mehrere Argumente durch Leerzeichen getrennt ausgibt.

Die ganzen Funktionen zum Umrechnen sind ziemlich gleich aufgebaut. Die Gemeinsamkeiten sollten nur einmal im Code stehen und sich nicht in jeder Funktion wiederholen. Das heisst Ein- und Ausgabe sollte nur in einer Funktion stehen, und nur die tatsächliche Umrechnung von einem Zahlwert in einen anderen, muss in eigene Funktionen. Letztlich ist das ein Fall für eine Datenstruktur. Beispielsweise ein Wörterbuch das Tupel aus zwei Einheiten auf eine Funktion abbildet die von der ersten Einheit in die Zweite umrechnet. Kann man hier auch einfach als ``lambda``-Ausdruck angeben.

Aus diesem Wörterbuch kann man dann auch die Auswahltexte generieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Vieles was Du an Code geschrieben hast, ist einfach nur kopiert und leicht verändert.
Sowohl bei der Ausgabe der Optionen, als auch beim Ausführen wird eine bestimmte Reihenfolge erwartet, wenn Du da mal etwas änderst, mußt Du immer darauf achten, dass das Konsistent bleibt.
Wenn Du irgend etwas an der Ergebnisausgabe ändern willst, mußt Du es an 6 Stellen tun.
Um solche Schwierigkeiten zu vermeiden, versucht man als Programmierer möglichst abstrakt zu bleiben. Da stellt man sich die Frage, welche Informationen brauche ich, um von einer Einheit in eine andere Umzurechnen. welche Information brauche ich für die Optionsauswahl und welche für die Ausgabe?

Heraus kommt, dass man für jede Temperatureinheit nur 4 Informationen braucht, die man dann am besten in einer Datenstruktur zusammenfasst. Vorteil: wenn man noch weitere Temperatureinheiten nach dem selben Muster hinzufügen will, braucht man nur eine Zeile zu ergänzen. Dem Rest des Programms ist es egal, was konkret berechnet werden soll. Mit der richtigen Formel kann man das selbe Programm benutzen um Äpfel in Birnen umzurechnen; Stichwort Wiederverwendbarkeit, es lohnt sich, Zeit zu investieren, weil man beim nächsten ähnlichen Problem Zeit sparen kann.

Code: Alles auswählen

from itertools import permutations
UNITS = [
    {"name": "Kelvin", "symbol": "K", "scale": 1, "offset": 0},
    {"name": "Celsius", "symbol": "°C", "scale": 1, "offset": 273.15},
    {"name": "Fahrenheit", "symbol": "°F", "scale": 5/9, "offset": 273.15-5/9*32},
]

def convert(temperature, from_unit, to_unit):
    temperature_in_kelvin = temperature *from_unit['scale'] + from_unit['offset']
    temperature_result = (temperature_in_kelvin - to_unit['offset']) / to_unit['scale']
    return temperature_result

def main():
    all_combinations = list(permutations(UNITS,2))
    while True:
        for idx, (from_unit, to_unit) in enumerate(all_combinations, 1):
            print(f" ({idx}) Umrechnung von {from_unit['name']} nach {to_unit['name']}")
        print(" (q) Programm beenden \n")
        wahl = input("Bitte wählen ")
        if wahl == 'q':
            return
        try:
            from_unit, to_unit = all_combinations[int(wahl)-1]
        except (ValueError, IndexError):
            print("Falsche Eingabe")
        else:
            temperature = float(input("Temperatur: "))
            temperature_result = convert(temperature, from_unit, to_unit)
            print(f"{temperature:.1f}{from_unit['symbol']} entsprechen {temperature_result:.1f}{to_unit['symbol']}")

if __name__ == '__main__':
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wörterbücher mit einem festen Satz an Schlüsseln sind ja im Grunde nicht das wofür Wörterbücher eigentlich gedacht sind: Eine Abbildung von Schlüsseln auf Werte bei der man solche Paare dynamisch hinzufügen und entfernen kann. Da es aber recht praktisch ist auch Daten mit festen Schlüsselsätzen so zu strukturieren und man die in der Form auch aus Dateien und von APIs zum Beispiel in Form von JSON recht häufig in die Finger bekommt, finde ich das (externe) `addict`-Modul ganz praktisch um solche Daten in Objekte zu verpacken, bei denen man per Attributzugriff an die Werte kommen kann. Der Code der das benutzt, sieht dann so aus als wären das normale Objekte, also das was man eigentlich bei einem festen Satz an Schlüsseln/Namen machen würde:

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import permutations

from addict import Dict

UNITS = [
    {"name": "Kelvin", "symbol": "K", "scale": 1, "offset": 0},
    {"name": "Celsius", "symbol": "°C", "scale": 1, "offset": 273.15},
    {
        "name": "Fahrenheit",
        "symbol": "°F",
        "scale": 5 / 9,
        "offset": 273.15 - 5 / 9 * 32,
    },
]


def convert(temperature, from_unit, to_unit):
    temperature_in_kelvin = temperature * from_unit.scale + from_unit.offset
    return (temperature_in_kelvin - to_unit.offset) / to_unit.scale


def main():
    all_combinations = list(permutations(map(Dict, UNITS), 2))
    while True:
        for i, (from_unit, to_unit) in enumerate(all_combinations, 1):
            print(
                f" ({i}) Umrechnung von {from_unit.name} nach {to_unit.name}"
            )
        print(" (q) Programm beenden \n")
        wahl = input("Bitte wählen ")
        if wahl == "q":
            return
        try:
            from_unit, to_unit = all_combinations[int(wahl) - 1]
        except (ValueError, IndexError):
            print("Falsche Eingabe")
        else:
            temperature = float(input("Temperatur: "))
            temperature_result = convert(temperature, from_unit, to_unit)
            print(
                f"{temperature:.1f}{from_unit.symbol} entsprechen"
                f" {temperature_result:.1f}{to_unit.symbol}"
            )


if __name__ == '__main__':
    main()
Das macht dann auch den Übergang leichter wenn man für eine Einheit tatsächlich mal einen `Unit`-Datentyp einführt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten