Script neustarten

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.
Hans888
User
Beiträge: 44
Registriert: Montag 10. Juni 2024, 08:58

Das sollte aber jetzt klappen.
Weis nur nicht ob es da eine feinere Lösung gibt :)

Code: Alles auswählen

    c= input("Wollen Sie das Programm beenden? (J/N): ")
    
    while True:
        if c == "J":
            break
        elif c == "N":
            print()
            break
        else:
            c != "N"
            print("Da ist eine Falsche Eingabe!")
            c= input("Wollen Sie das Programm beenden? (J/N): ")
    if c == "J":    
        break
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

Was bezweckst Du mit `c != "N"`?
Wenn man die selbe Zeile mehrfach kopiert, dann gibt es meist eine bessere Lösung.

Code: Alles auswählen

    while True:
        c = input("Wollen Sie das Programm beenden? (J/N): ")
        if c in ["J", "N"]:
            break
        print("Da ist eine falsche Eingabe!")
    if c == "J":    
        break
Hans888
User
Beiträge: 44
Registriert: Montag 10. Juni 2024, 08:58

@snafu und Sirius3
Vielen Dank für eure Hilfe.
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier mit weiteren Änderungen:
- Liste anstatt Wörterbuch, weil das in der Handhabung in diesem Kontext einfacher ist
- Einführung zahl_als_text() sowie Umbenennung kommazahl() in text_als_zahl()
- Kein Absturz mehr bei Division durch Null

Code: Alles auswählen

#!/usr/bin/env python3
from decimal import Decimal, DivisionByZero, InvalidOperation
from operator import add, mul, sub, truediv

OPERATIONEN = [
    ("Addition", add),
    ("Subtraktion", sub),
    ("Multiplikation", mul),
    ("Division", truediv),
]

EINGABEFEHLER = "FEHLER: Ungültige Eingabe"

def zeige_überschrift(text):
    print(f"\n{text.upper()}\n{'=' * len(text)}")

def erfrage_wert(abfrage, konverter):
    while True:
        try:
            return konverter(input(abfrage))
        except ValueError:
            print(EINGABEFEHLER)

def erfrage_auswahl(menüpunkte):
    for nummer, (menüpunkt, _) in enumerate(menüpunkte, 1):
        print(f"({nummer}) {menüpunkt}")
    while True:
        index = erfrage_wert("Ihre Auswahl: ", int) - 1
        if not 0 <= index < len(menüpunkte):
            print("FEHLER: Menüpunkt nicht vorhanden")
        else:
            return menüpunkte[index]

def text_als_zahl(text):
    try:
        return Decimal(text.replace(",", "."))
    except InvalidOperation:
        raise ValueError(text) from None

def zahl_als_text(zahl):
    return str(zahl).replace(".", ",")

def erfrage_zahlen():
    a = erfrage_wert("Geben Sie die erste Zahl ein: ", text_als_zahl)
    b = erfrage_wert("Geben Sie die zweite Zahl ein: ", text_als_zahl)
    return a, b

def berechne(a, b, operation):
    ergebnis = operation(a, b)
    ganzzahl = int(ergebnis)
    if ergebnis == ganzzahl:
        return ganzzahl
    return ergebnis

def zeige_ergebnis(a, b, operation):
    try:
        ergebnis = berechne(a, b, operation)
    except DivisionByZero:
        print("FEHLER: Division durch Null")
    else:
        print("Das Ergebnis ist:", zahl_als_text(ergebnis))

def erfrage_ende(ja, nein):
    abfrage = f"Wollen Sie das Programm beenden ({ja}/{nein})? "
    while (ende := erfrage_wert(abfrage, str.lower)) not in (ja, nein):
        print(EINGABEFEHLER)
    return ende == ja

def main():
    while True:
        zeige_überschrift("Hauptmenü")
        menüpunkt, operation = erfrage_auswahl(OPERATIONEN)
        zeige_überschrift(menüpunkt)
        a, b = erfrage_zahlen()
        zeige_ergebnis(a, b, operation)
        if erfrage_ende("j", "n"):
            break

if __name__ == "__main__":
    try:
        main()
    except (KeyboardInterrupt, EOFError):
        pass
@Hans888: So siehst du, wie ein simpel erscheinendes Mathe-Programm nach und nach an Komplexität zunimmt und verstärkt in Funktionen aufgeteilt wird, damit man den Überblick behält. Eine Protokollierung der Änderungen, wie ich das oben gemacht habe, gehört üblicherweise auch dazu.
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man kann das auch lustig weiterspinnen, z. B. das Menü und die einzelnen Punkte objektorientiert programmieren. Den Rechner selbst würde ich funktional belassen. Einmal weil man sich IMHO leichter in diese Struktur reindenken kann. Und zweitens, weil es im Großen und Ganzen bloß eine Aneinanderreihung von statischen Methoden wäre und damit keine echte Klasse darstellt.

Ich hab das Programm nun mal auf Basis von "dataclasses" so implementiert:

Code: Alles auswählen

#!/usr/bin/env python3
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from decimal import Decimal, DivisionByZero, InvalidOperation
from operator import add, mul, sub, truediv

EINGABEFEHLER = "FEHLER: Ungültige Eingabe"

@dataclass
class Menüpunkt:
    text: str
    operation: Callable


@dataclass
class Menü:
    inhalt: Sequence[Menüpunkt]

    def __str__(self):
        zeilen = [self.erzeuge_überschrift()]
        zeilen.extend(
            f"({nummer}) {menüpunkt.text}"
            for nummer, menüpunkt in enumerate(self.inhalt, 1)
        )
        return "\n".join(zeilen)

    def erfrage_auswahl(self):
        while True:
            index = erfrage_wert("Ihre Auswahl: ", int) - 1
            if not 0 <= index < len(self.inhalt):
                print("FEHLER: Menüpunkt nicht vorhanden")
            else:
                return self.inhalt[index]

    @staticmethod
    def erzeuge_überschrift(menüpunkt=None):
        überschrift = "Hauptmenü" if menüpunkt is None else menüpunkt.text
        return f"\n{überschrift.upper()}\n{'=' * len(überschrift)}"

    @classmethod
    def aus_modell(cls, modell):
        return cls([Menüpunkt(text, operation) for text, operation in modell])


def erfrage_wert(abfrage, konverter):
    while True:
        try:
            return konverter(input(abfrage))
        except ValueError:
            print(EINGABEFEHLER)

def text_als_zahl(text):
    try:
        return Decimal(text.replace(",", "."))
    except InvalidOperation:
        raise ValueError(text) from None

def zahl_als_text(zahl):
    return str(zahl).replace(".", ",")

def erfrage_zahlen():
    a = erfrage_wert("Geben Sie die erste Zahl ein: ", text_als_zahl)
    b = erfrage_wert("Geben Sie die zweite Zahl ein: ", text_als_zahl)
    return a, b

def berechne(a, b, operation):
    ergebnis = operation(a, b)
    ganzzahl = int(ergebnis)
    if ergebnis == ganzzahl:
        return ganzzahl
    return ergebnis

def zeige_ergebnis(a, b, operation):
    try:
        ergebnis = berechne(a, b, operation)
    except (DivisionByZero, ZeroDivisionError):
        print("FEHLER: Division durch Null")
    else:
        print("Das Ergebnis ist:", zahl_als_text(ergebnis))

def erfrage_ende(ja, nein):
    abfrage = f"Möchten Sie das Programm beenden ({ja}/{nein})? "
    while (ende := erfrage_wert(abfrage, str)) not in (ja, nein):
        print(EINGABEFEHLER)
    return ende == ja


def main():
    menü = Menü.aus_modell([
        ("Addition", add),
        ("Subtraktion", sub),
        ("Multiplikation", mul),
        ("Division", truediv),
    ])
    while True:
        print(menü)
        menüpunkt = menü.erfrage_auswahl()
        print(menü.erzeuge_überschrift(menüpunkt))
        a, b = erfrage_zahlen()
        zeige_ergebnis(a, b, menüpunkt.operation)
        if erfrage_ende("j", "n"):
            break

if __name__ == "__main__":
    try:
        main()
    except (KeyboardInterrupt, EOFError):
        pass
Fazit: Nette Spielerei, aber beim gegenwärtigen Stand des Programms hält sich der Mehrwert der beiden Klassen in Grenzen. Soll heißen, das war jetzt nicht wirklich notwendig, um es verständlicher und übersichtlicher werden zu lassen.
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: meiner Meinung nach packst Du in erfrage_ende zu viel in eine Zeile.

Code: Alles auswählen

def erfrage_ende():
    abfrage = "Wollen Sie das Programm beenden (j/n)? "
    while True:
        ende = erfrage_wert(abfrage, str.lower))
        if ende in ("j", "n"):
            return ende == "j"
        print(EINGABEFEHLER)
Hans888
User
Beiträge: 44
Registriert: Montag 10. Juni 2024, 08:58

Ohje da muss ich noch viel lernen um das alles zu verstehen. Danke schon mla für eure Unterstützung.
ich hoffe ich werde das alles bald mal verstehen :)
LeSchakal
User
Beiträge: 25
Registriert: Dienstag 5. Februar 2019, 23:40

snafu hat geschrieben: Montag 1. Juli 2024, 05:17

Code: Alles auswählen

    @staticmethod
    def erzeuge_überschrift(menüpunkt=None):
        überschrift = "Hauptmenü" if menüpunkt is None else menüpunkt.text
        return f"\n{überschrift.upper()}\n{'=' * len(überschrift)}"
Hier wäre wohl schöner:

Code: Alles auswählen

überschrift = menüpunkt.text if menüpunkt else "Hauptmenü"
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@LeSchakal
Oder noch kürzer (aber auch kryptischer):

Code: Alles auswählen

überschrift = getattr(menüpunkt, "text", "Hauptmenü")
Ich teste halt lieber explizit auf None, damit so Sachen wie 0, False, [] oder andere als unwahr ausgewertete Übergaben einen Fehler werfen.
Benutzeravatar
__blackjack__
User
Beiträge: 14002
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Iiih `dataclasses`. Damit fängt man sich Typannotationen ein. Und die dann nur so halbherzig (Callable) machen ist zusätzlich unschön. Mit `attrs` kann man die Typannotationen weglassen.

Der angesprochene Test ist ja eigentlich nur nötig weil eine eigentlich recht generische *Funktion* in eine Klasse gesteckt wurde und zusätzlich etwas über Menüpunkte wissen muss.

Das `berechne()` aus `zeige_ergebnis()` aufgerufen wird ist IMHO falsch. Damit macht `zeige_ergebnis()` mehr als der Name vermuten lässt, und im Hauptprogramm steht dann nur noch die Aufrufabfolge `erfrage_werte()` und `zeige_ergebnis()`, wo man ja eigentlich dazwischen erwarten würde das eine Berechnung stattfinden würde. Letztlich würde ich die `zeige_ergebnis()` „inlinen“, weil man sonst die Trennung zwischen Programmlogik und Benutzerinteraktion komplizierter machen müsste. Was ja wahrscheinlich der Grund ist warum Du die Aufrufe so unerwartet verschachtelt hast.

`text_als_zahl()` und `zahl_als_text()` sind keine Namen die Tätigkeiten beschreiben.

Bei `erfrage_ende()` ist das schräg das der Prompt nicht übergeben wird (womit die Funktion allgemeiner wird) aber die Werte für `ja` und `nein`. Das macht ja am meisten Sinn wenn man mal die Sprache der Kommunikation mit dem Benutzer ändern möchte, dann muss man aber in dieser Funktion den Prompt übersetzen, und an ganz anderer Stelle im Programm die Werte für Ja und Nein. Diese Werte sind ansonsten ja auch nicht wirklich variabel. Ich würde hier entweder die Frage(-Vorlage) auch als Argument nehmen, oder die Antwortmöglichkeiten in der Funktion festklopfen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from decimal import Decimal, DivisionByZero, InvalidOperation
from operator import add, mul, sub, truediv

from attrs import frozen, field

EINGABEFEHLER = "FEHLER: Ungültige Eingabe"


def erzeuge_ueberschrift(text):
    if "\n" in text:
        raise ValueError(f"line ending within {text!r}")

    return f"\n{text.upper()}\n{'=' * len(text)}"


@frozen
class Menuepunkt:
    text = field()
    operation = field()

    def als_ueberschrift(self):
        return erzeuge_ueberschrift(self.text)


@frozen
class Menue:
    menuepunkte = field()

    def __str__(self):
        return "\n".join(
            [
                erzeuge_ueberschrift("Hauptmenü"),
                *(
                    f"({nummer}) {menuepunkt.text}"
                    for nummer, menuepunkt in enumerate(self.menuepunkte, 1)
                ),
            ]
        )

    def erfrage_auswahl(self):
        while True:
            index = erfrage_wert("Ihre Auswahl: ", int) - 1
            if 0 <= index < len(self.menuepunkte):
                return self.menuepunkte[index]

            print("FEHLER: Menüpunkt nicht vorhanden")

    @classmethod
    def aus_modell(cls, modell):
        return cls([Menuepunkt(text, operation) for text, operation in modell])


def parse_zahl(text):
    try:
        return Decimal(text.replace(",", "."))
    except InvalidOperation:
        raise ValueError(text) from None


def formatiere_zahl(zahl):
    return str(zahl).replace(".", ",")


def erfrage_wert(abfrage, konverter=str):
    while True:
        try:
            return konverter(input(abfrage))
        except ValueError:
            print(EINGABEFEHLER)


def erfrage_zahlen():
    return [
        erfrage_wert(
            f"Geben Sie die {ordnungszahlwort} Zahl ein: ", parse_zahl
        )
        for ordnungszahlwort in ["erste", "zweite"]
    ]


def berechne(erster_operand, zweiter_operand, operation):
    ergebnis = operation(erster_operand, zweiter_operand)
    ganzzahl = int(ergebnis)
    if ergebnis == ganzzahl:
        return ganzzahl
    return ergebnis


def erfrage_ende(fragevorlage, ja, nein):
    frage = fragevorlage.format(ja, nein)
    antworten = {ja.casefold(), nein.casefold()}
    while True:
        antwort = erfrage_wert(frage).casefold()
        if antwort in antworten:
            return antwort == ja.casefold()

        print(EINGABEFEHLER)


def main():
    menue = Menue.aus_modell(
        [
            ("Addition", add),
            ("Subtraktion", sub),
            ("Multiplikation", mul),
            ("Division", truediv),
        ]
    )
    while True:
        print(menue)
        menuepunkt = menue.erfrage_auswahl()

        print(menuepunkt.als_ueberschrift())
        erster_operand, zweiter_operand = erfrage_zahlen()
        try:
            ergebnis = berechne(
                erster_operand, zweiter_operand, menuepunkt.operation
            )
        except (DivisionByZero, ZeroDivisionError):
            print("FEHLER: Division durch Null")
        else:
            print("Das Ergebnis ist:", formatiere_zahl(ergebnis))

        if erfrage_ende("Möchten Sie das Programm beenden ({}/{})?", "j", "n"):
            break


if __name__ == "__main__":
    try:
        main()
    except (KeyboardInterrupt, EOFError):
        pass
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@__blackjack__:
Bei ``menüpunkt.als_überschrift()`` bin ich definitiv bei dir, die restlichen Punkte sehe ich nicht so wie du bzw. würde sie mal als Ansichtssache einordnen.
Antworten