Neuling im Forum hat mal ne Frage :p

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.
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

Hay liebe Python-Fan-Gemeinde :),

seit kurzem hat auch mich Python in seinen Bann gezogen. Diese Sprache ist wirklich total genial. Nun ich lerne jetzt seit ca. 1 Woche Python mit einem Buch (Einstieg in Python von Thomas Theis) und bin auch sehr zufrieden mit dem Buch. Nachdem ich jetzt die Grundlegenden Sachen erst einmal gelernt habe, dachte ich mir ich schreibe erst mal ein paar mini Sachen für Konsole um alles zu verinnerlichen. Da das ganze natürlich nur sinnvoll ist, wenn man jemanden drüber schauen lässt der auch Ahnung hat, habe ich mich hier angemeldet um mir von euch bei Problem oder Fragen helfen lassen zu können.

So, da ich heute Mittag einen mini rechner gebastelt habe (nur + und -) wollte ich mal fragen, wo ich den poste wenn ich euch mal drüber schauen lassen möchte um mir eure Verbesserungen an zu hören (Bzw zu lesen :P) mit ich mir auch einen anständigen Programmierstil an eigene und vor allem nichts falsches lerne!

Na ja, und wo ich hier gerade schon mal poste hänge ich jetzt noch meinen mini Rechnercode ran ;o

Hier ist er

Code: Alles auswählen

# rechner v 0.1
# Additon und Subtraktion, solange bis Nutzer beendet

# Funktion zum Einlesen eines Wertes
def eingabe() :
    fehler = 1
    # für Eingaben wiederholung bei Fehler
    while fehler == 1 :
        print("Bitte geben Sie eine Zahl ein :")
        try :
            x = float(input())
            fehler = 0
        except :
            print("Sie haben keine Zahl eingegeben!")
        # Wert an Aufruf zurück geben
        # Wert wird nur zurück gegebn wenn Umwandlung erfolgreich war
        if fehler == 0 :
            return x

# Variable für die Auswahl
wahl = -1
# Begrüßung
print("Willkommen beim Rechner von mir :)")
# while Schleife für Wiederholung solange User möchte
while wahl != 0 :
    print("Bitte entscheiden Sie sich für eine Rechenart :")
    print("-0- Beenden")
    print("-1- Addition (+)")
    print("-2- Subtraktion (-)")
    # Auswahl der Rechenart
    try :
        wahl = int(input())
    except :
        print("Sie haben keine erlaubte Option genutzt.")
        continue
    # if elif zur Auswahl der Rechenart
    if wahl == 1 :
        a = eingabe()
        b = eingabe()
        print(a, " + ", b , " = ", a+b)
    elif wahl == 2 :
        a = eingabe()
        b = eingabe()
        print(a, " - ", b , " = ", a-b)
    else :
        print("Sie haben einen Fehler bei der Auswahl gemacht.")
        print("Bitte versuchen Sie es erneut.")
        continue
Mit freundlichen Grüßen Metalcore
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo und Willkommen im Forum!

Grundsätzlich sieht das schon mal ganz gut aus :-)

Du benutzt Python 3? Das solltest Du in Zukunft durchaus erwähnen, da es zwischen der noch stark verbreiteten 2er Version und der 3er Unterschiede gibt.

Generell würde ich immer dieses Grundgerüst wählen:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

def main():
    # hier ist der Einstieg in Dein Programm

if __name__ == "__main__":
    main()
Damit hast Du keinen Code auf Modulebene. Wenn Du Dein Beispiel in einer Python-Shell (oder einem anderen Script) importierst, wird der komplette "untere" Teil ab `wahl = -1` sofort ausgeführt. Würdest Du das einfach in eine main-Funktion schreiben, wie oben angedeutet, passiert das nicht. Damit kannst Du einfacher in einer Shell einzelne Funktionen während der Entwicklung austesten oder das ganze für andere Scripte später wieder verwenden (z.B. Deine `eingabe`-Funktion).

Der Shebang ist nur für Unixoide Systeme interessant, damit das Script direkt gestartet werden kann. Das Encoding kannst Du frei wählen, ich würde einfach utf-8 wählen. Wichtig ist es jedoch, das Encoding explizit anzugeben, damit der Python-Interpreter weiß, wie er String-Literale aus Deinem Quellcode intern in Unicode wandeln kann. Mehr zu diesem imho recht schwierig zu kapierenden Thema findest Du in den Links in meiner Sig.

Nun direkt zu Deinem Code.

Verwende doch anstelle von reinen Kommentaren oftmals eher Docstrings:

Code: Alles auswählen

def eingabe():
    """
    Funktion zum Einlesen eines float-Wertes. Gibt diesen zurück.
    """
Zudem vermeide triviale Kommentare wie "Begrüßung" - das ist beim Lesen der Codezeile mit der print-Funktion offensichtlich und muss nicht dokumentiert werden ;-) Grundsätzlich ist es löblich, dass Du Dich da bemühst, aber zu viele triviale Kommentare erschweren eher das Lesen.
Gutes Kommentieren ist sowieso fast das schwierigste am Coden! Beschreibe in einem Kommentar, was getan wird, nicht wie. Denn das wie ist ja der Code. In Ausnahmefällen ist es natürlich hilfreich auf komplexe Codestrukturen auf in Kommentaren einzugehen.

Es gibt für Wahrheitswerte `True` und `False` in Python. Ich würde daher `fehler = True` setzen und nicht mit einem Integer hantieren. Sofern die Umwandlung klappt, kannst Du den Wert doch direkt zurückgeben!

Code: Alles auswählen

    while True:
        print("Bitte geben Sie eine Zahl ein :")
        try :
            return float(input())
        except ValueError:
            print("Sie haben keine Zahl eingegeben!")
Wie Du siehst kann man sich so den Namen `fehler` sparen. Die Schleife wird immer betreten und ist immer eine Endlosschleife. Die kann man nur verlassen, wenn die Eingabe einer float-Variablen erfolgt.

Du solltest auch nie einfach nur ein `except` schreiben! Benenne immer die Ausnahme, die Du auswerten / abfangen willst. Ansonsten kann es dazu kommen, dass eine andere, von Dir nicht bedachte Ausnahme auftritt, und Du diese einfach schluckst. Das macht das Debuggen schwieriger, da Dir die Ursache verschleiert wird. Das gilt dann auch weiter unten im Code bei Dir.

Ich würde die Auswahl der Rechenoperationen von der eigentlichen Berechnung trennen. Schreibe doch für die Auswahl ebenso eine Funktion, die Du es für die Zahleneingabe schon getan hast ;-) Der Rückgabewert entscheidet dann über die aufzurufende Funktionalität.

Um das ganze flexibler zu machen, kann man so ein Menü und die Verquickung von Option und zugehöriger Funktion (=Dispatching) auch mit einer geeigneten Datenstruktur, wie einer Liste oder einem Dictionary erledigen. Da Du noch stark am Anfang stehst, solltest Du Dich erst einmal mit denen befassen, bevor Du dieses Problem angehst. Du findest dazu zig Lösungen und Ansätze hier im Forum; vermutlich reicht dazu schon das Suchwort "Taschenrechner" ;-)

Viel Spaß noch beim Lernen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo und herzlich willkommen im Forum!

Habe mir mal Dein Beispiel angeschaut. Folgendes ist mir aufgefallen:
  • Vor Doppelpunkten gehört kein Leerschritt!
  • Die Eingabeschleife finde ich etwas unglücklich gelöst. Warum verlässt Du diese bei erfolgreicher Eingabe nicht gleich per 'return'?
    Zudem solltest Du bei der 'try: except:'-Schleife auch den möglichen Fehler angeben. Gib' einmal CTRL-C ein: Eigentlich erzeugt das einen 'KeyboardInterrupt', der so aber nicht abgefangen wird. Mit anderen Worten: Du fängst jeden Fehler ab, obwohl es Dir ja nur einen 'ValueError' abfangen möchtest.
  • Du hast für Eingaben eine eigene Funktion gebaut, was so ja auch gut und richtig ist. Aber warum verwendest Du diese Funktion dann nicht auch für die Eingabe der Auswahl?
  • Auch die Auswahl-Schleife ist nicht so optimal... :wink: Was soll hier hinter dem 'try:' denn versucht werden? Und welchen Fehler möchtest Du hinter 'except:' denn abfangen um dann doch per 'continue' fortzufahren? Alles sehr verworren und unnötig verschachtelt...
  • Gewöhne Dir an, Formatstrings zu verwenden. Scheint anfangs etwas umständlich zu sein, ist aber gerade später bei etwas umfangreicheren Ausgaben echt von Vorteil...
  • Überlege Dir einmal, wie man das, was hinter 'wahl == 1', 'wahl = 2' und dann später ja noch den anderen Rechenarten soweit vereinheitlichen könnte, dass Du das ebenfalls in wiederverwendbare Funktionen stecken kannst. Denn das Menü, die Eingaben und dann das Berechnen der Ergebnisse sollte voneinander getrennt sein...
mutetella


Hier mein Vorschlag:

Code: Alles auswählen

def eingabe(prompt):
    while True:
        try:
            x = float(input(prompt))
            return x
        except ValueError:
            print("Sie haben keine Zahl eingegeben!\n")

def main():
    print("Willkommen beim Rechner von mir :)")
    while True:
        print("Bitte entscheiden Sie sich fuer eine Rechenart :")
        print("-0- Beenden")
        print("-1- Addition (+)")
        print("-2- Subtraktion (-)")
        wahl = eingabe('Ihre Auswahl bitte: ')
        if wahl == 1 :
            a = eingabe('Links:  ')
            b = eingabe('Rechts: ')
            print('{0} + {1} = {2}\n'.format(a, b, a+b))
        elif wahl == 2 :
            a = eingabe('Links:  ')
            b = eingabe('Rechts: ')
            print('{0} - {1} = {2}\n'.format(a, b, a-b))
        elif wahl == 0:
            print('Danke und auf Wiedersehen...')
            break


if __name__ == '__main__':
    main()
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@mutetella: Wieso bindest Du den umgewandelten Zahlenwert noch an `x`? ;-) Erfolgt beim Vergleich eines float-Wertes mit einem Integer ein implizites Casting auf Integer? Ich kenne mich hier mit Python3 nicht gut genug aus; grundsätzlich finde ich es jedoch nicht so prickelnd, eine Eingabe für float-Werte auch für Integerwerte zu nutzen; evtl. übergibt man die Casting-Funktion einfach an die Eingabefunktion?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Hyperion:
Nun ja, ich würde das auch eher so lösen:

Code: Alles auswählen

def eingabe(prompt, type_):
    while True:
        try:
            return type_(raw_input(prompt))
        except ValueError:
            print("Fehlerhafte Eingabe.\n")
Ich wollte aber nicht so weit weg vom ursprünglichen Beispiel gehen...

mutetella


EDIT: Wobei man sich am Text 'Fehlerhafte Eingabe.' auch noch so richtig auslassen könnte... :wink:
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

Danke erst mal für euren sehr ausführlichen Antworten! :)

Ich hätte aber mal eine Frage bezüglich eurer eingabe Funktion. Und zwar was und wofür ist dieses prompt?
Und ist es richtig wenn ich denke, das type_ dafür da ist das dynamisch darauf reagiert wird, ob der eingegebene Zahlenwert ein int oder float Wert ist?

Ich nutze Python 3.2 und im Moment ein Xubuntu 11.04 System. Als Editor hab ich bisher immer IDLE genutzt, was für sinnvolle Alternativen gibt es da?

Und was mir jetzt noch nicht ganz klar ist, ist das hier :

Code: Alles auswählen


#!/usr/bin/env python
# coding: utf-8

def main():
    # hier ist der Einstieg in Dein Programm

if __name__ == "__main__":
    main()
 
die Ersten beiden Zeilen ist klar.
Erste ist bereits bekannt von Bash-Skripten, allerdings nicht für Python gewusst.
Zweite verstehe ich auch noch, aber ich verstehe jetzt nicht so ganz den Vorteil der Main-Funktion.
Soll das dann einen Aufbau wie bei z.B. C++ sozusagen nachempfinden und dafür Sorgen, das wirklich nur die Main-Funktion in der Reihenfolge wie sie aufgebaut ist abgearbeitet werden?

Edit :

So habe jetzt mal etwas erweitert und nach bestem wissen verbessert.
- Habe die Ausgabe/Berechnung auch ausgelagert
- Die Eingabe-Funktion überarbeitet
- alle 4 Grundrechenarten eingebaut

Also den Main-Funktion Aufbau habe ich mal noch außen vor gelassen, da ich das noch nicht so ganz verstanden habe.
Hier mal mein überarbeiteter Code :)

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

# Taschenrechner v 0.2
# 4 Grundrechenarten, solange bis Nutzer beendet

# Funktion zum Einlesen eines Wertes
def eingabe():
    # für Eingaben wiederholung bei Fehler
    while True:
        print("Bitt Eingabe machen:")
        try:
            return float(input())
        except ValueError:
            print("Sie haben eine fehlerhafte Eingabe gemacht!\n")

#Funktion für die Ausgabe
def ausgabe(zahl,zahl1,wahl,op):
    if wahl == 1:
        erg = zahl+zahl1
    elif wahl == 2:
        erg = zahl-zahl1
    elif wahl == 3:
        erg = zahl*zahl1
    elif wahl == 4:
        erg = zahl/zahl1
    print(zahl, op , zahl1, " = ", erg)

print("Willkommen beim Rechner von mir :)")
# Wiederholung solange User möchte
while True:
    print("Bitte entscheiden Sie sich für eine Rechenart :")
    print("-0- Beenden")
    print("-1- Addition (+)")
    print("-2- Subtraktion (-)")
    print("-3- Multiplikation (*)")
    print("-4- Subtraktion (/)")
    # Auswahl der Rechenart
    wahl = eingabe()
    
     #Auswahl der Rechenart
    if wahl == 0:
        print("Danke und bis bald\n")
        break
    elif wahl == 1:
        a = eingabe()
        b = eingabe()
        op = " + "
        ausgabe(a,b,wahl,op)
    elif wahl == 2:
        a = eingabe()
        b = eingabe()
        op = " - "
        ausgabe(a,b,wahl,op)
    elif wahl == 3:
        a = eingabe()
        b = eingabe()
        op = " * "
        ausgabe(a,b,wahl,op)
    elif wahl == 4:
        a = eingabe()
        b = eingabe()
        op = " / "
        ausgabe(a,b,wahl,op)
    else:
        print("Sie haben einen Fehler gemacht!\n")
BlackJack

@Metalcore: Schau doch einfach wo `prompt` innerhalb der Funktion verwendet wird. Also dann in der Dokumentation von `input()`. Das nicht fest in der Funktion zu kodieren macht sie flexibler, weil man bei verschiedenen Eingaben, dem Benutzer auch verschiedene Aufforderungen geben kann. In Deiner Funktion würde ich das `print()` vor dem ``try`` auch weglassen und die Aufforderung der `input()`-Funktion übergeben.

Bei `type_` genau so: Schau wo es verwendet wird. Das Objekt wird mit der Eingabe als Argument aufgerufen und ihr Rückgabewert wird zurück gegeben. Vergleiche welches Objekt Du in Deinem Quelltext an der Stelle aufrufst, dann weisst Du auch was man für `type_` übergeben kann.

Die `main()`-Funktion sorgt dafür, dass möglichst wenig Code direkt auf Modulebene steht. Da sollte man möglichst nur Importe und Definitionen von Konstanten, Funktionen, und Klassen stehen haben. Wenn man dort mehr stehen hat, besteht eher die Gefahr, dass man aus versehen oder absichtlich aus Funktionen auf Objekte zugreift, die nicht als Argument in die Funktion hinein gekommen sind. Das macht Programme schwerer verständlich, und schlechter test- und wartbar.

Die letzten beiden Zeilen von dem `main()`-Idiom sorgen dafür, dass die `main()`-Funktion nur dann aufgerufen wird, wenn das Modul als Programm ausgeführt wird. Denn nur dann ist der Name `__name__` an die Zeichenkette '__main__' gebunden. Sonst ist dieser Name an den eine Zeichenkette mit dem Namen des Moduls gebunden. Mit anderen Worten kann man das Modul dann importieren ohne dass das Programm losläuft. Zum Beispiel um in einer Python-Shell einzelne Funktionen zu testen, oder in anderen Modulen wieder zu verwenden, oder automatisierte Tests auszuführen und so weiter.

Beim Quelltext sind noch ein paar überflüssige Kommentare. Die über `eingabe()` und `ausgabe()` sind zum Beispiel mehr als trivial. Wobei die Funktion mehr macht als sie vom Namen her andeutet. Sie gibt nicht nur aus, sondern rechnet auch. Bei der Aufteilung so wie sie jetzt ist, wiederholt sich die Entscheidungsstruktur für den Wert von `wahl` im Hauptteil und der `ausgabe()`-Funktion. Wenn man eine weitere Rechenart hinzufügen möchte, muss man das Programm an den zwei Stellen ändern. Das liesse sich aber auf eine beschränken.

Im Hauptteil wiederholt sich zu viel innerhalb der einzelnen Zweige. Wenn Rechnungen ausgeführt werden, dann unterscheiden sich die Zweige nur in einer einzigen Zeile: Der Zuweisung an `op`. Also sollte man die anderen nicht immer und immer wieder hinschreiben müssen.

Was dann für Rechnungen bleibt ist, das die Zahl `wahl` auf eine Zeichenkette abgebildet wird. Dafür sind ``if``/``elif`` ziemlich überdimensioniert. Man könnte die Zeichenketten auch in eine Liste stecken und dann `wahl` entsprechend angepasst als Index für den Zugriff verwenden.

In diese Liste könnte man dann nicht nur das Symbol für die Rechenoperation stecken, sondern auch die Operation selbst. Die Operatoren von Python gibt es im `operators`-Modul auch alle noch einmal als Funktionen.

Schlussendlich könnte man in die Liste auch noch eine Zeichenkette mit dem Namen der Rechenart in jedes Element stecken und daraus die Menüausgabe generieren. Um eine weitere Rechenart hinzuzufügen braucht man dann nur noch eine Funktion, welche zwei Argumente entgegen nimmt und einen weiteren Eintrag in der Liste mit den Rechenoperationen und der Rest des Programms bleibt vollkommen gleich. So ein Eintrag in der Liste könnte Beispielsweise so aussehen: ``('Addition', '+', operators.add)``.
Zuletzt geändert von BlackJack am Freitag 14. Oktober 2011, 08:26, insgesamt 1-mal geändert.
Grund: Doppeleinfügen repariert.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack:
Du solltest beim Tippen Dein Touchpad ausschalten... :P
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

Servus,
betreffend main Funktion hat dir Hyperion ja schon die Antwort gegeben:
Damit hast Du keinen Code auf Modulebene. Wenn Du Dein Beispiel in einer Python-Shell (oder einem anderen Script) importierst, wird der komplette "untere" Teil ab `wahl = -1` sofort ausgeführt. Würdest Du das einfach in eine main-Funktion schreiben, wie oben angedeutet, passiert das nicht. Damit kannst Du einfacher in einer Shell einzelne Funktionen während der Entwicklung austesten oder das ganze für andere Scripte später wieder verwenden (z.B. Deine `eingabe`-Funktion).
Übrigens, ich habe das gleiche Buch und bin auch gerade am kämpfen ;)

@hyperion, mutetella, Blackjack: BTW. was mich interessieren würde, wie lange programmiert ihr eigentlich schon?

LG
Daniel
Zuletzt geändert von mcdaniels am Freitag 14. Oktober 2011, 08:28, insgesamt 1-mal geändert.
BlackJack

@mutetella: Schön wenn's so einfach wäre. Es war kein Touchpad, sondern meine Maus. Die mittlere Maustaste löst in letzter Zeit immer häufiger mehrfach aus wenn ich sie drücke. :-(
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@BlackJack: Ich wollte dem OP nicht gleich direkt mit einer Listenlösung kommen, da er ja noch ein kompletter "Frischling" ist. Sofern er keine andere Sprache vorher kannte, muss man sich sicher erst einmal in Datenstrukturen wie Listen reindenken. Aber vielleicht hast Du ja Recht, wenn Du ihm schon mal ein Konstrukt zeigst und so eine neue Motivation gibst, neues zu erlernen :-)

Wir sollten mal eine Taschenrechner-Wiki-Seite bauen, auf der wir die typischen Evolutionsschritte für dieses Anfängerproblem veranschaulichen :-D

@mcdaniels: Angefangen habe ich 1990 auf nem C64 mit BASIC. 2003 habe ich das erste Mal Kontakt mit Python bekommen; seit 2007 iirc habe ich mich aber erst wirklich intensiv mit der Sprache und deren Idiomen auseinandergesetzt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mcdaniels
User
Beiträge: 168
Registriert: Mittwoch 18. August 2010, 19:53

@hyperion: Muss schon toll sein, wenn man sich aus dem Handgelenk etwas programmieren kann. So weit werd ichs wohl nicht bringen, befürchte ich... aber wieder back on topic, bevor ich noch den Thread übernehme, was ja nicht sein soll!

@TO: Bin gespannt, wie es dir geht, wenn du das Kapitel mit der Objektorientierung erreicht hast... ;-)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Naja, beim Autofahren merkt man irgendwann auch nicht mehr bewusst, wie man kuppelt und schaltet. Das ist zwar kein perfekter Vergleich, da Programmieren dann doch etwas weniger subtil abläuft, aber trotzdem ist es im Prinzip reine Übung. Und zwar die Übung des Denkens in der Programmiersprache, sowie die Kenntnisse zu den Modulen und Funktionen der Sprache.
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

@ mcdaniels
Na ja, ich habe bisher nur Kapitel 3 gelesen ^^' zu mehr bin ich bisher nicht gekommen und am Wochenende sieht es auch schlecht aus, da meine Freundin bei mir ist.

Ich denke ich hab das ganze jetzt so weit verstanden, sollte ich bevor sie heute Abend kommt es schaffen, werde ich meinen Code noch mal mit meinen neuen Erkenntnissen verbessern.(Oder zu mindestens versuchen :mrgreen: )
BlackJack hat geschrieben:@Metalcore: Schau doch einfach wo `prompt` innerhalb der Funktion verwendet wird. Also dann in der Dokumentation von `input()`. Das nicht fest in der Funktion zu kodieren macht sie flexibler, weil man bei verschiedenen Eingaben, dem Benutzer auch verschiedene Aufforderungen geben kann. In Deiner Funktion würde ich das `print()` vor dem ``try`` auch weglassen und die Aufforderung der `input()`-Funktion übergeben.

Bei `type_` genau so: Schau wo es verwendet wird. Das Objekt wird mit der Eingabe als Argument aufgerufen und ihr Rückgabewert wird zurück gegeben. Vergleiche welches Objekt Du in Deinem Quelltext an der Stelle aufrufst, dann weisst Du auch was man für `type_` übergeben kann.

Die `main()`-Funktion sorgt dafür, dass möglichst wenig Code direkt auf Modulebene steht. Da sollte man möglichst nur Importe und Definitionen von Konstanten, Funktionen, und Klassen stehen haben. Wenn man dort mehr stehen hat, besteht eher die Gefahr, dass man aus versehen oder absichtlich aus Funktionen auf Objekte zugreift, die nicht als Argument in die Funktion hinein gekommen sind. Das macht Programme schwerer verständlich, und schlechter test- und wartbar.

Die letzten beiden Zeilen von dem `main()`-Idiom sorgen dafür, dass die `main()`-Funktion nur dann aufgerufen wird, wenn das Modul als Programm ausgeführt wird. Denn nur dann ist der Name `__name__` an die Zeichenkette '__main__' gebunden. Sonst ist dieser Name an den eine Zeichenkette mit dem Namen des Moduls gebunden. Mit anderen Worten kann man das Modul dann importieren ohne dass das Programm losläuft. Zum Beispiel um in einer Python-Shell einzelne Funktionen zu testen, oder in anderen Modulen wieder zu verwenden, oder automatisierte Tests auszuführen und so weiter.

Beim Quelltext sind noch ein paar überflüssige Kommentare. Die über `eingabe()` und `ausgabe()` sind zum Beispiel mehr als trivial. Wobei die Funktion mehr macht als sie vom Namen her andeutet. Sie gibt nicht nur aus, sondern rechnet auch. Bei der Aufteilung so wie sie jetzt ist, wiederholt sich die Entscheidungsstruktur für den Wert von `wahl` im Hauptteil und der `ausgabe()`-Funktion. Wenn man eine weitere Rechenart hinzufügen möchte, muss man das Programm an den zwei Stellen ändern. Das liesse sich aber auf eine beschränken.

Im Hauptteil wiederholt sich zu viel innerhalb der einzelnen Zweige. Wenn Rechnungen ausgeführt werden, dann unterscheiden sich die Zweige nur in einer einzigen Zeile: Der Zuweisung an `op`. Also sollte man die anderen nicht immer und immer wieder hinschreiben müssen.

Was dann für Rechnungen bleibt ist, das die Zahl `wahl` auf eine Zeichenkette abgebildet wird. Dafür sind ``if``/``elif`` ziemlich überdimensioniert. Man könnte die Zeichenketten auch in eine Liste stecken und dann `wahl` entsprechend angepasst als Index für den Zugriff verwenden.

In diese Liste könnte man dann nicht nur das Symbol für die Rechenoperation stecken, sondern auch die Operation selbst. Die Operatoren von Python gibt es im `operators`-Modul auch alle noch einmal als Funktionen.

Schlussendlich könnte man in die Liste auch noch eine Zeichenkette mit dem Namen der Rechenart in jedes Element stecken und daraus die Menüausgabe generieren. Um eine weitere Rechenart hinzuzufügen braucht man dann nur noch eine Funktion, welche zwei Argumente entgegen nimmt und einen weiteren Eintrag in der Liste mit den Rechenoperationen und der Rest des Programms bleibt vollkommen gleich. So ein Eintrag in der Liste könnte Beispielsweise so aussehen: ``('Addition', '+', operators.add)``.
Ich wollte nur noch kurz darauf hinweisen, das ich bisher, wirklich nur minimale Grundkenntnisse habe, also wirklich nur das was ich in meinem Programm verwendet habe, habe ich bis jetzt in Python gelernt (Da ich noch nicht weiter bin im Buch aus Zeitmangel.) Ich weiß nicht ob es dir was sagt, aber habe halt bisher nur das Kapitel 3 im Buch Einstieg in Python gelesen. Die Teile des Kapitel die behandelt wurden sind :
Verzweigungen
Schleifen
Fehler und Ausnahmebehandlungen (Minimale Ausführungen)
Funktionen und Module

Also bitte nicht gleich zu viel Informationen auf einmal, könnte sonst zu Missverständnissen kommen ^^'

Mit freundlichen Grüßen metalcore

EDIT :

So ich habe doch noch mal eine wenig Zeit gefunden und habe es wieder so gut ich konnte verbessert.

Habe mir die Vorschläge von euch noch einmal alle durchgelesen und so gut es geht meinen Code verbessert.
- Main-Funktion Übernommen
- Versucht Kommentare zu reduzieren
- Die im Hauptteil sich zu oft wiederholenden Teile der Zweige verbessert

Sonst fällt mir gerade nichts ein was ich gemacht habe ^^'.
Ich habe mir den Vorschlag mit den Listen natürlich auch vor genommen. Allerdings erschien mir das ganze zu komplex als das ich das zwischen Aufräumen und Duschen eben machen wollte. Ich werde mir das Thema Sonntagabend wenn ich wieder Zeit habe zur Brust nehmen und genauer anschauen. Genauso wie prompt und type__ .

So hier jetzt noch mal mein Code, dies mal hoffentlich wieder etwas besser als vorher :)

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

# Taschenrechner v 0.2
# 4 Grundrechenarten, solange bis Nutzer beendet

def eingabe():
    # für Eingaben wiederholung bei Fehler
    while True:
        print("Bitt Eingabe machen:")
        try:
            return float(input())
        except ValueError:
            print("Sie haben eine fehlerhafte Eingabe gemacht!\n")

""" Funktion übernimmt Rechnung und Ausgabe"""
def ausgabe(zahl,zahl1,wahl,op):
    if wahl == 1:
        erg = zahl+zahl1
    elif wahl == 2:
        erg = zahl-zahl1
    elif wahl == 3:
        erg = zahl*zahl1
    elif wahl == 4:
        erg = zahl/zahl1
    print(zahl, op , zahl1, " = ", erg)

def main():
    
    print("Willkommen beim Rechner von mir :)")
    # Wiederholung solange User möchte
    while True:
        print("Bitte entscheiden Sie sich für eine Rechenart :")
        print("-0- Beenden")
        print("-1- Addition (+)")
        print("-2- Subtraktion (-)")
        print("-3- Multiplikation (*)")
        print("-4- Subtraktion (/)")
        
        wahl = eingabe() # Auswahl der Rechenart

        if wahl == 0:
            print("Danke und bis bald\n")
            break
        elif wahl == 1:
            op = " + "
        elif wahl == 2:
            op = " - "
        elif wahl == 3:
            op = " * "
        elif wahl == 4:
            op = " / "
        else:
            print("Sie haben einen Fehler gemacht!\n")

        a = eingabe()
        b = eingabe()  
        ausgabe(a,b,wahl,op)

        """ Aufruf von der Funktion Ausgabe mit Übergabe
        der Parameter für Rechnung und Ausgabe """
    

if __name__ == "__main__":
    main()
BlackJack

@mcdaniels: Bei mir war es auch BASIC auf dem C64. Erste Kontakte 1986 in der Schule (allerdings nicht in Form von Unterricht, sondern wir „kleinen“ durften Nachmittags in dem Rechnerraum) und ab 1988 dann mit einem eigenen C64.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Metalcore:
Damit es Dir Sonntagabend nicht langweilig wird hab' ich in Deinen Taschenrechner einmal 'ganz sachte' eine Liste eingebaut, über die Du Deine Auswahl triffst.
Hier erstmal der geänderte Code, meine Änderungen habe ich nummeriert und unten erklärt:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

# 1.
MENU_ITEMS = (('', 'Beenden'),
              ('+', 'Addition'),
              ('-', 'Subtraktion'),
              ('*', 'Multiplikation'),
              ('/', 'Division'))

def eingabe():
    # für Eingaben wiederholung bei Fehler
    while True:
        print("Bitte Eingabe machen:")
        try:
            return float(input())
        except ValueError:
            print("Sie haben eine fehlerhafte Eingabe gemacht!\n")

""" Funktion übernimmt Rechnung und Ausgabe"""
# 5.
def ausgabe(zahl, zahl1, op):
    if op == '+':
        erg = zahl+zahl1
    elif op == '-':
        erg = zahl-zahl1
    elif op == '*':
        erg = zahl*zahl1
    elif op == '/':
        erg = zahl/zahl1
    print(zahl, op , zahl1, " = ", erg)

def main():
    print("Willkommen beim Rechner von mir :)")
    while True:
        print("Bitte entscheiden Sie sich für eine Rechenart :")
        # 2.
        for choice, item in enumerate(MENU_ITEMS):
            print('-{0}- {1} ({2})'.format(choice, item[1], item[0]))
       
        # 3.
        wahl = int(eingabe()) # Auswahl der Rechenart

        if wahl == 0:
            print("Danke und bis bald\n")
            break
        elif wahl > len(MENU_ITEMS)-1:
            print("Sie haben einen Fehler gemacht!\n")

        # 6.
        a = eingabe()
        # 6.
        b = eingabe()  
        # 4.
        op = MENU_ITEMS[wahl][0]
        ausgabe(a, b, op)

if __name__ == '__main__':
    main()
  1. 'MENU_ITEMS' ist ein Tuple, das zu jeder Auswahlmöglichkeit ein weiteres Tuple mit den nötigen Infos enthält.
  2. Somit kannst Du über 'MENU_ITEMS' iterieren und aus jedem 'item' einen Menüpunkt erstellen. 'enumerate()' ist eine kleine aber feine built-in-Funktion, die Dir den Index eines jeden Elements liefert. Ohne 'enumerate()' müsstest Du daher selbst einen Index-Zähler implementieren. Beispiel:

    Code: Alles auswählen

    >>> liste = ['Null', 'Eins', 'Zwei']
    >>> index = 0
    >>> for element in liste:
    ...     print(index, element)
    ...     index += 1
    0 Null
    1 Eins
    2 Zwei
    >>> for index, element in enumerate(liste):
    ...     print(index, element)
    0 Null
    1 Eins
    2 Zwei
  3. Das ist z. B. ein Grund, weshalb Deine 'eingabe()'-Funktion flexibler gestaltet werden sollte. Könnte ich den Typ, den ich zurückerhalten möchte als Parameter gleich übergeben, müsste ich aus dem float hier nicht wieder ein int machen.
    Muss ich deshalb, weil ich ja über 'wahl' auf 'MENU_ITEMS' zugreifen möchte. Und ein Index muss ein int sein.
  4. Das, was Du über die elendlange if-/elif-Abfrage gemacht hast, erreichst Du hier mit einem Zugriff auf 'MENU_ITEMS'. Du holst Dir das Element mit der Zahl, auf das 'wahl' verweist und aus diesem Element dann wiederum das vorderste. Beispiel:

    Code: Alles auswählen

    >>> MENU_ITEMS = (('', 'Beenden'),
    ...('+', 'Addition'),
    ...('-', 'Subtraktion'),
    ...('*', 'Multiplikation'),
    ...('/', 'Division'))
    >>> MENU_ITEM[1]
    ('+', 'Addition')
    >>> _[0]  #Mit _ kannst Du in der Pythonshell immer auf das letzte Ergebnis zugreifen
    '+'
    >>> MENU_ITEM[1][0]
    '+'
  5. Somit musst Du 'wahl' auch nicht mehr an Deine 'ausgabe()'-Funktion übergeben. Dass Du innerhalb von 'ausgabe()' aber trotzdem noch sage und schreibe 4 Abfragen durchlaufen musst, kannst Du durch noch konsequenteren Einsatz von 'MENU_ITEMS' vermeiden.
    BlackJack hat Dir da ja schon was dazu geschrieben... :wink:
  6. Und wenn Du jetzt noch einen dynamischen Prompt einbaust, dann kannst Du bei der Abfrage der Zahlen auch einen Text mitgeben, damit man weiß, was man eingeben soll. 3 x hintereinander 'Bitte Eingabe machen:' ist ja nicht so dolle...
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

Danke dir mutetella :)

Ich hab jetzt auf deinem Post mal geschaut wie und wo Tupel in meinem Buch vorkommen. Zufälligerweise ist dies das nächste Kapitel (Nr 4). Daher werde ich mich jetzt erst mal durch Kapitel 4 arbeiten und dann meinen Rechner wieder mit dem neu gelerntem verbessern (hoffe ich zu mindestens :P)

Ich danke auch allen anderen für ihre Hilfe ! Habe dadurch direkt schon ein paar Sachen gelernt und gesehen wie ich meinen Programmierstil verbessern kann :) (z.B. diese unnötigen Kommentare von mir ...^^')

Ich hoffe es klappt weiterhin so gut :)

Also bis bald denke ich ;)

Mit freundlichen Grüßen Metalcore


ps: Ds Forum gefällt mir, habe schon viele andere Foren dieser Art gesehen, wo Neulingen nicht so nett geholfen wurde :)


EDIT :
Ok konnte doch nicht warten, habe mich jetzt mit Prompt und type_ beschäftigt und es jetzt verstanden.(glaube ich)

habe jetzt noch mal verbessert mit prompt und type_ :

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

# Taschenrechner v 0.2
# 4 Grundrechenarten, solange bis Nutzer beendet

def eingabe(prompt, type_):
    # für Eingaben wiederholung bei Fehler
    while True:
        try:
            return type_(input(prompt))
        except ValueError:
            print("Sie haben eine fehlerhafte Eingabe gemacht!\n")

""" Funktion übernimmt Rechnung und Ausgabe"""
def ausgabe(zahl,zahl1,wahl,op):
    if wahl == 1:
        erg = zahl+zahl1
    elif wahl == 2:
        erg = zahl-zahl1
    elif wahl == 3:
        erg = zahl*zahl1
    elif wahl == 4:
        erg = zahl/zahl1
    print(zahl, op , zahl1, " = ", erg)

def main():
    
    print("Willkommen beim Rechner von mir :)")
    # Wiederholung solange User möchte
    while True:
        print("Folgende Rechenarten stehen zur Auswahl:")
        print("-0- Beenden")
        print("-1- Addition (+)")
        print("-2- Subtraktion (-)")
        print("-3- Multiplikation (*)")
        print("-4- Subtraktion (/)")
        
        wahl = eingabe("Bitte wählen Sie eine Rechenart:\n", int)
        # Auswahl der Rechenart

        if wahl == 0:
            print("Danke und bis bald\n")
            break
        elif wahl == 1:
            op = " + "
        elif wahl == 2:
            op = " - "
        elif wahl == 3:
            op = " * "
        elif wahl == 4:
            op = " / "
        else:
            print("Sie haben einen Fehler gemacht!\n")

        a = eingabe("Bitte erste Zahl eingeben :\n", float)
        b = eingabe("Bitte zweite Zahl eingeben :\n", float)  
        ausgabe(a,b,wahl,op)

        """ Aufruf von der Funktion Ausgabe mit Übergabe
        der Parameter für Rechnung und Ausgabe """
    

if __name__ == "__main__":
    main()
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

So nachdem ich jetzt heute mal wieder etwas mehr Zeit gefunden habe, hab ich mal wieder weiter gemacht und wieder ein wenig neues gelernt. Leider sind es bis jetzt nur Listen geworden und ein paar Funktion für Objekte vom Typ String. Aber jetzt sind ja erst mal 2 Wochen Ferien, da werde ich sicherlich mehr Zeit investieren können.

Ich habe wieder einmal probiert meinen Code etwas zu verbessern, bzw habe ihn neu geschrieben (etwas Übung im tippen schadet ja nicht, bei so kurzem Code)
- möglichst wenig Kommentare (also versucht mich auf das wesentliche und nötige zu beschränken)
- Menü via Liste
- Variablen via Liste (Da sie ja, so wie ich es verstanden habe, die gleiche Funktion wie Arrays haben, hielt ich das für sinnvoll)
- Beenden über Frage mit Antwort von ja(j) oder nein(j)
- Auswahl der Operatoren über Objekt vom Typ String

So hier noch mein (hoffentlich) wieder ein wenig besserer Code ^^ :

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8

# Taschenrechner v 0.2.7
# 4 Grundrechenarten, Benutzergesteuerte Wiederholung/Ende

def eingabe(prompt, type_):
# Eingabe wird bei Fehler automatisch wiederholt
    while True:
        try:
            return type_(input(prompt))
        except ValueError:
            print("Ein Fehler bei der Eingabe wurde bemerkt!\n")

def auswertung(a, b , c):       # Funktion zur Ausgabe und Berechnung
    if a == "+":
        erg = b+c
    elif a == "-":
        erg = b-c
    elif a == "*":
        erg = b*c
    elif a == "/":
        erg = b/c
        
    print(b, a , c , "=" , erg)

def main():
    while True:
        
        menu = ["Willkommen zum Rechner v0_2_7",
                "+ Addition", "- Subtraktion","* Multiplikation",
                "/ Division"]

        var = ["op", 0, 0, "n"]
# Ausgabe des Menüs  
        for x in range(0,5):
            print(menu[x])
# Überprüfung und Eingabe des Operators
        op = True
        while op == True:
            var[0] = eingabe("Bitte einen Operator auswählen :\n", str)
            if var[0] == "+":
                op = False
            elif var[0] == "-":
                op = False
            elif var[0] == "*":
                op = False
            elif var[0] == "/":
                op = False
            else:
                print("Sie haben einen Fehler gemacht!\n")

        var[1] = eingabe("Bitte geben Sie eine Zahl ein :\n", float)
        var[2] = eingabe("Bitte geben Sie eine Zahl ein :\n", float)

        auswertung(var[0],var[1],var[2])

        var[3] = eingabe("Möchten Sie weiter rechnen? (j / n)\n", str)
        if var[3].startswith("n") and var[3].endswith("n"):
            print("Aufwiedersehen")
            break
        if var[3].startswith("j") and var[3].endswith("j"):
            print()
        else:
            print("Sie haben einen Fehlergemacht")
            print("Das Programm wird fortgesetzt!\n")

if __name__ == "__main__":
    main()
edit :
ich hoffe es stört niemanden das ich meinen alten Thread dafür wieder nehme?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Versuche doch nun noch, nicht nur die Menüeinträge in einer Liste zu verwalten, sondern auch das Dispatching darüber laufen zu lassen. Im `operator`-Modul gibt es Funktionen für alle Grundrechenarten; damit kannst Du direkt über die Eingabe auf die auszuführende Funktion verzweifen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Metalcore
User
Beiträge: 8
Registriert: Donnerstag 13. Oktober 2011, 14:17

Hyperion hat geschrieben:Versuche doch nun noch, nicht nur die Menüeinträge in einer Liste zu verwalten, sondern auch das Dispatching darüber laufen zu lassen. Im `operator`-Modul gibt es Funktionen für alle Grundrechenarten; damit kannst Du direkt über die Eingabe auf die auszuführende Funktion verzweifen.
Ok danke für den Tipp, das werde ich mir ansehen.
Antworten