Taschenrechner

Code-Stücke können hier veröffentlicht werden.
Antworten
isi.one
User
Beiträge: 4
Registriert: Dienstag 16. August 2022, 22:57

Code: Alles auswählen

def Taschenrechner():
    
    eingabe1 = eval(input("erste Zahl eingeben: "))                                            

    operator = input("wähle (+), (-), (*), (/): ")
    
    eingabe2 = eval(input("zweite Zahl eingeben: "))

    if operator == "+":
        print("Ergebnis = ", eingabe1 + eingabe2)
        
    if operator == "-":
        print("Ergebnis = ", eingabe1 - eingabe2)
        
    if operator == "*":
        print("Ergebnis = ", eingabe1 * eingabe2)
        
    if operator == "/":
        print("Ergebnis = ", eingabe1 / eingabe2)

Taschenrechner()
Hallo zusammen, bin hier neu im Forum.

Bin ein Anfänger, und würde mich freuen, wen mir jemand helfen würde mich weiter zu entwickeln.

wie kann ich diesen Taschenrechner verbessern , wie z,b den code kürzer zu gestalten.

ich bin dankbar für jede Hilfe und Vorschlag.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@isi.one: Das wichtigste wäre hier das Du `eval()` vergisst. Das verwendet man nicht, schon gar nicht um einfach nur eine eingegebene Zeichenkette in eine Zahl zu wandeln. `eval()` führt beliebige Python-Ausdrücke aus. Im schlimmsten Fall ist das eine Sicherheitslücke, weil da auch was eingegeben werden kann, was beispielsweise die Dateien vom Benutzer unter dem das Programm läuft, gelöscht werden. Aber auch was die Fehlerbehandlung angeht ist das ein Albtraum, weil bei beliebigen Ausdrücken natürlich auch beliebige Ausnahmen auftreten können. Wenn man die vorgesehene `float()`-Funktion verwendet, muss man sich nur mit einem `ValueError` beschäftigen, falls der Benutzer etwas eingibt, das nicht in eine Zahl umgewandelt werden kann.

Den Aufruf der Funktion sollte man davor schützen das er auch passiert wenn das Modul nicht als Programm ausgeführt, sondern nur importiert wird. Aus einem anderen Modul, beispielsweise um die Funktion wiederzuverwenden, oder automatisiert zu testen (Unit-Tests), oder in einer interaktiven Python-Shell, um die Funktion auszuprobieren, oder Fehler zu suchen. Es gibt auch Werkzeuge die Module importieren und sich darauf verlassen, dass dabei keine Seiteneffekte passieren, sondern nur Konstanten, Funktionen, und Klassen definiert werden. Zum Beispiel um den Code zu analysieren, oder Dokumentation aus dem Quelltext zu ziehen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Funkionen (und Methoden) werden nicht nur komplett klein geschrieben, sondern üblicherweise auch nach der Tätigkeit benannt die sie durchführen, damit der Leser eine Idee hat, was da passiert, und um sie von eher passiven Werten unterscheiden zu können. `Taschenrechner` wäre also eher ein Name für eine Klasse und `taschenrechner` der Name für einen Wert der einen Taschenrechner als Objekt repräsentiert.

Hier würde ich einfach den traditionellen Namen für die Hauptfunktion eines Programms verwenden: `main()`.

Man nummeriert keine Namen. Das ist hier in gewisser Weise ein Sonderfall, weil man zwei mehr oder weniger gleichberechtigte Werte als Operanden hat, aber auch hier würde ich nicht einfach eine Nummer anhängen. Ich würde das entweder als Zahlworte in den Namen aufnehmen, oder als relative Positionsangabe zum Operator, also `erster_operand` und `zweiter_operand` oder `linker_operand` und `rechter_operand`. So wie man das in natürlicher Sprache ausdrücken würde.

`operator` kann nur einen der vier Werte annehmen, auf die dort mit ``if`` getestet wird. Das ist also ein Fall für ``elif``-Anweisungen nach dem ersten ``if``.

Und dann hat man ein ``if``-Konstrukt das mit einem ``elif`` endet. An der Stelle sollte man sich immer Fragen ob man den ``else``-Fall einfach so ignorieren sollte. In diesem Fall würde das bedeuten, dass der Benutzer einen falschen Operator eingegeben hat. Darauf könnte/sollte man ihn vielleicht hinweisen.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3


def main():
    #
    # TODO Fehlerbehandlung bei den Eingaben.
    #
    erster_operand = float(input("erste Zahl eingeben: "))
    operator = input("wähle (+), (-), (*), (/): ")
    zweiter_operand = float(input("zweite Zahl eingeben: "))
    #
    # TODO Test auf `operator` durch ein Wörterbuch ersetzen, dass die
    # Operatorsymbole auf die entsprechenden Funktionen aus dem `operator`-Modul
    # abbildet.
    #
    # TODO Nicht "Ergebnis =" in jedem Zweig wiederholen.
    #
    if operator == "+":
        print("Ergebnis =", erster_operand + zweiter_operand)

    elif operator == "-":
        print("Ergebnis =", erster_operand - zweiter_operand)

    elif operator == "*":
        print("Ergebnis =", erster_operand * zweiter_operand)

    elif operator == "/":
        #
        # TODO Fehlerbehandlung wenn der zweite Operand 0 ist.
        #
        print("Ergebnis =", erster_operand / zweiter_operand)

    else:
        print("Unbekannter Operator:", repr(operator))


if __name__ == "__main__":
    main()
Da sind noch ein paar TODOs als Kommentare drin. Bei Eingaben von Benutzern sollte man immer mit allem Möglichen rechnen. Von Tippfehlern, über ehrliche Verständnisfehler/Missverständnisse, beispielsweise das einer hier denkt man müsse die Klammern bei den Operatoren mit eingeben, oder das Dezimalbrüche mit dem im deutschen üblichen Dezimalkomma eingegeben werden können; bis zu Benutzer die einfach mal die Grenzen ausloten wollen mit welchen Eingaben man das wohl zum Absturz bringen könnte.

Da zweimal eine Zahl eingebeben werden muss, sollte mindestens das in einer eigenen Funktion stehen die zweimal aufgerufen wird, statt den Code dafür zu wiederholen.

Im `operator`-Modul in der Standardbibliothek gibt es eine Funktion für jeden Operator den Python kennt. Statt die Auswahl einer Rechenoperation aufgrund einer eingegebenen Zeichenkette durch ``if`` mit Code zu treffen, könnte man das über eine Datenstruktur lösen, welche die Symbole auf entsprechende Funktionen abbildet.

Damit wird man dann auch die Wiederholung von der Zeichenkette "Ergebnis =" in jedem Zweig los. Code- und Datenwiederholungen vermeidet man als Programmierer. Wenn man mal Änderungen vornehmen will oder muss, dann muss man bei Kopien von Code und/oder Daten immer daran denken das an allen Stellen zu ändern, und auch gleich, oder zumindest gleichwertig tun. Das ist unnötige Arbeit und eine Fehlerquelle die man vermeiden kann.

Beim Teilen durch 0 kann eine Ausnahme auftreten die man behandeln sollte.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
isi.one
User
Beiträge: 4
Registriert: Dienstag 16. August 2022, 22:57

Code: Alles auswählen

def main():
    
    erste_eingabe = float(input("erste Zahl eingeben: "))                                            

    operator = input("Addieren (+), Subtrahieren (-), Multiplizieren (*), Dividieren (/): ")
    
    zweite_eingabe = float(input("zweite Zahl eingeben: "))

    
    
    if operator == "+":
        print(erste_eingabe + zweite_eingabe)
        
    elif operator == "-":
        print(erste_eingabe - zweite_eingabe)
        
    elif operator == "*":
        print(erste_eingabe * zweite_eingabe)
        
    elif operator == "/":
        print(erste_eingabe / zweite_eingabe)
    

    else:
        print("\n")
        if not operator == "+" or "-" or "*" or "/":
            print("ungültiger operator:", repr(operator))
    
    print("Ergebnis")
    

if __name__ == "__main__":
    main()

@__blackjack__, Danke für die rasche Antwort und die Aufklärung.
habe versucht es um zu setzen, immerhin ist die Funktion da.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Warum hast du denn jetzt den else-Zweig so komisch gemacht? Alle Operatoren wurden doch mit den (el)if-Blöcken schon abgefragt. Insofern ist ein nochmaliges Überprüfen bzw Ausschließen überflüssig. Außerdem tut dieses or-Konstrukt nicht das, was du offenbar erwartest. Man würde das stattdessen mit dem in-Operator überprüfen.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@isi.one: ``if not operator == "+" or "-" or "*" or "/":`` macht keinen Sinn. Erst einmal *weiss* man auch ohne einen Test in dem ``else`` bereits, dass ein ungültiger Operator verwendet wurde, denn sonst wäre der Programmfluss nicht in dem ``else`` angekommen, sondern hätte einen der Zweige davor abgearbeitet. Und dann ist diese Bedingung *immer* wahr, egal welchen Wert `operator` hat:

Code: Alles auswählen

In [53]: operator = "anything"

In [54]: not operator == "+" or "-" or "*" or "/"
Out[54]: True

In [55]: operator = "+"

In [56]: not operator == "+" or "-" or "*" or "/"
Out[56]: '-'

In [57]: bool(not operator == "+" or "-" or "*" or "/")
Out[57]: True

In [58]: operator = "-"

In [59]: not operator == "+" or "-" or "*" or "/"
Out[59]: True
``or`` ist kein umgangssprachliches „oder“, sondern ein binärer Operator wie ``+``, ``*``, ``/`` und so weiter, mit einem linken Operanden und einem rechten Operanden. Und genau wie andere Operatoren gibt es Vorrangregeln. Also das Punkt- vor Strichrechnung kommt. Vergleichsoperatoren wie ``==`` haben eine höhere Priorität als logische Operatoren wie ``or``. Und auch ``not`` hat eine höhere Priorität. Wenn man 1+2*3 hat, dann kann/muss man sich ja implizit Klammern wie folgt vorstellen: ``1+(2*3)``. Bei der Bedingung sähe das so aus was da tatsächlich ausgewertet wird: ``(((not (operator == "+")) or "-") or "*") or "/"``. Die zweiten Operanden von allen ``or``-Operationen sind nicht-leere Zeichenketten, und damit ”wahr”, wodurch der gesamte Ausdruck immer ”wahr” ist.

Code: Alles auswählen

In [63]: True or "x"
Out[63]: True

In [64]: False or "x"
Out[64]: 'x'

In [65]: bool(True or "x")
Out[65]: True

In [66]: bool(False or "x")
Out[66]: True
Hier mal meine letzte Version um ein Wörterbuch erweitert, statt die Wahl mit Code zu lösen, und das Abfragen der Benutzereingaben in Funktionen herausgezogen, wo allerdings immer noch eine Behandlung von Falscheingaben fehlt:

Code: Alles auswählen

#!/usr/bin/env python3
from operator import add, mul as multiply, sub as subtract, truediv as divide


def erfrage_zahl(aufforderungs_text):
    #
    # TODO Fehlerbehandlung bei der Eingabe.
    #
    return float(input(aufforderungs_text))


def erfrage_operator(operatoren):
    #
    # TODO Fehlerbehandlung bei der Eingabe.
    #
    operatoren_text = ", ".join(f"({operator})" for operator in operatoren)
    return input(f"wähle {operatoren_text}: ").strip()


def main():
    operator_auf_funktion = {
        "+": add,
        "-": subtract,
        "*": multiply,
        "/": divide,
    }
    erster_operand = erfrage_zahl("erste Zahl eingeben: ")
    operator = erfrage_operator(operator_auf_funktion.keys())
    zweiter_operand = erfrage_zahl("zweite Zahl eingeben: ")

    funktion = operator_auf_funktion.get(operator)
    if funktion:
        #
        # TODO Fehlerbehandlung beim Teilen durch 0.
        #
        print("Ergebnis =", funktion(erster_operand, zweiter_operand))
    else:
        print("Unbekannter Operator:", repr(operator))


if __name__ == "__main__":
    main()
Diese Variante hat den Vorteil, dass die Operatorsymbole nur noch einmal im Quelltext stehen müssen, man dort also keinen Fehler mehr machen kann, dass man bei der Frage nach dem Operator und beim prüfen der Operatoren unterschiedliche Operatoren schreiben könnte. Das ist auch einfach(er) erweiterbar, weil man zusätzliche Operatoren nur noch in das Wörterbuch eintragen muss, und nicht in die Eingabeaufforderung und in die Auswertung.

Nachteil ist hier jetzt, dass da die Erweiterung der Eingabeaufforderung für den Operator nicht den Namen der Operation enthält, wie das in Deinem letzten Beitrag zu sehen ist. Um den Code dadurch zu erweitern, müsste der Name der Operation neben der Funktion für die Operation als Wert in dem Wörterbuch gespeichert werden. Zum Beispiel in dem die Werte dort Tupel aus Name und Funktion sind. Lesbarer und mit etwas weniger Änderungen bei der Auswertung der Eingaben verbunden, wäre ein `collections.namedtuple`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
isi.one
User
Beiträge: 4
Registriert: Dienstag 16. August 2022, 22:57

@snafu
stimmt an den in-Operator habe ich nicht gedacht :D .
Wie ist das gemeint , das der else-block komisch ist ?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Das `if` im `else`-Block ist komisch, also unsinnig und überflüssig, aber das hat ja __blackjack__ schon ausführlich beschrieben.
isi.one
User
Beiträge: 4
Registriert: Dienstag 16. August 2022, 22:57

1.Danke @__blackjack__ 2.Danke @snafu

ich bin noch ziemlich am Anfang, wird wohl seine Zeit brauchen bis ich in die (PythonWelt) mich einiger maßen zurecht finden werde.

Ich arbeite mich in die Materie ein.
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Jetzt mit Operationen die über eine Beschreibung, das Operatorsymbol, und die dazu passende Funktion beschrieben werden, um die Beschreibung in der Operatorauswahl zu haben:

Code: Alles auswählen

#!/usr/bin/env python3
from collections import namedtuple
from operator import add, mul as multiply, sub as subtract, truediv as divide

Operation = namedtuple("Operation", "beschreibung operator funktion")

OPERATIONEN = [
    Operation("addieren", "+", add),
    Operation("subtrahieren", "-", subtract),
    Operation("multiplizieren", "*", multiply),
    Operation("dividieren", "/", divide),
]
OPERATOR_AUF_OPERATION = {
    operation.operator: operation for operation in OPERATIONEN
}
assert len(OPERATIONEN) == len(OPERATOR_AUF_OPERATION)


def erfrage_zahl(aufforderungstext):
    #
    # TODO Fehlerbehandlung bei der Eingabe.
    #
    return float(input(aufforderungstext))


def erfrage_operator(operationen):
    #
    # TODO Fehlerbehandlung bei der Eingabe.
    #
    operationen_text = ", ".join(
        f"{operation.beschreibung.capitalize()} ({operation.operator})"
        for operation in operationen
    )
    return input(f"wähle {operationen_text}: ").strip()


def main():
    erster_operand = erfrage_zahl("erste Zahl eingeben: ")
    operator = erfrage_operator(OPERATIONEN)
    zweiter_operand = erfrage_zahl("zweite Zahl eingeben: ")

    operation = OPERATOR_AUF_OPERATION.get(operator)
    if operation:
        #
        # TODO Fehlerbehandlung beim Teilen durch 0 (und andere Ausnahmen die
        # auftreten können).
        #
        print(
            "Ergebnis =", operation.funktion(erster_operand, zweiter_operand)
        )
    else:
        print("Unbekannter Operator:", repr(operator))


if __name__ == "__main__":
    main()
Neu ist hier das zusammenfassen von mehreren Werten zu einem Objekt mittels `collections.namedtuple()`.

Für die Zahleneingabe die Falscheingaben sinnvoll behandelt und teilen durch 0 (und andere Rechenfehler) die man noch angehen könnte (siehe TODO-Kommentare) wäre das Thema Ausnahmen und Ausnahmebehandlung Grundlage.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das erfragen einer Zahl vom Benutzer um Fehlerbehandlung ergänzt, so dass so lange nachgefragt wird, bis tatsächlich eine gültige Zahl eingegeben wurde. Wobei vor dem Umwandeln der Eingabe noch Kommas durch Punkte ersetzt werden, damit der Benutzer auch das im deutschsprachigen Raum übliche Komma als Dezimalbruchtrennzeichen eingeben kann, wenn er möchte:

Code: Alles auswählen

def erfrage_zahl(aufforderungstext):
    while True:
        try:
            return float(input(aufforderungstext).replace(",", "."))
        except ValueError:
            print("Fehler: Bitte eine Zahl eingeben!")
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hier mal einen Schritt weiter und eine Fehlerbehandlung in das erfragen der Operation eingebaut. Es wird von der Funktion auch nicht mehr der Operator zurückgegeben, sondern direkt das `Operation`-Objekt.

Code: Alles auswählen

#!/usr/bin/env python3
from collections import namedtuple
from operator import add, mul as multiply, sub as subtract, truediv as divide

Operation = namedtuple("Operation", "beschreibung operator funktion")

OPERATIONEN = [
    Operation("addieren", "+", add),
    Operation("subtrahieren", "-", subtract),
    Operation("multiplizieren", "*", multiply),
    Operation("dividieren", "/", divide),
]
OPERATOR_AUF_OPERATION = {
    operation.operator: operation for operation in OPERATIONEN
}
assert len(OPERATIONEN) == len(OPERATOR_AUF_OPERATION)


def erfrage_zahl(aufforderungstext):
    while True:
        try:
            return float(input(aufforderungstext).replace(",", "."))
        except ValueError:
            print("Fehler: Bitte eine Zahl eingeben!")


def erfrage_operation(operationen):
    operationen_text = ", ".join(
        f"{operation.beschreibung.capitalize()} ({operation.operator})"
        for operation in operationen
    )
    aufforderungstext = f"wähle {operationen_text}: "
    while True:
        operator = input(aufforderungstext).strip()
        try:
            return OPERATOR_AUF_OPERATION[operator]
        except KeyError:
            print(f"Fehler: Unbekannter Operator {operator!r}.")


def main():
    erster_operand = erfrage_zahl("erste Zahl eingeben: ")
    operation = erfrage_operation(OPERATIONEN)
    zweiter_operand = erfrage_zahl("zweite Zahl eingeben: ")
    #
    # TODO Fehlerbehandlung beim Teilen durch 0 (und andere Ausnahmen die
    # auftreten können).
    #
    print("Ergebnis =", operation.funktion(erster_operand, zweiter_operand))


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Um das mal zum Abschluss zu bringen, das letzte TODO aus den Kommentaren umgesetzt: Die Fehlerbehandlung bei der Berechnung des Ergebnisses:

Code: Alles auswählen

def main():
    erster_operand = erfrage_zahl("erste Zahl eingeben: ")
    operation = erfrage_operation(OPERATIONEN)
    zweiter_operand = erfrage_zahl("zweite Zahl eingeben: ")
    try:
        print(
            "Ergebnis =", operation.funktion(erster_operand, zweiter_operand)
        )
    except ZeroDivisionError:
        print("Es ist nicht möglich durch 0 zu teilen!")
    except OverflowError:
        print(
            "Das Ergebnis dieser Rechnung ist ausserhalb des Wertebereichs von"
            " Gleitkommazahlen!"
        )
    except ArithmeticError as error:
        print("Es ist ein Rechenfehler aufgetreten:", error)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten