(geschlossen)Counter innerhalb einer Funktion?

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
Benutzeravatar
Schaf220
User
Beiträge: 113
Registriert: Montag 11. August 2008, 16:00
Wohnort: Bremen
Kontaktdaten:

Hallo an alle,
ich hab da mal ein Probelm an dem ich verzeifle!
Ich arbeite an einem kleinen Matheprogramm und hab für die vier Rechenarten jeweils eine Funktion geschrieben.
Wenn der Buntzer eine Aufgabe richtig löst dann printet er Bravo und falls er falsch antwortet, gibt es erst ne Meldung für Falsch und ne Aufforderung das richtige Ergebnis ein zu geben danach soll ein counter hoch zählen nur das klappt nicht so richtig obwohl ich den counter auf den globalen Namensraum bezogen habe.

Hier mal eine Funktion:

Code: Alles auswählen

def plus(one, two):
    global counter
    counter = 0
    try:
        once = one + two
        print one, '+', two, '\n'
        task = input('Ihr Ergebnis: ')
        while(task != once):
            task = input("""Ihr vorheriges Ergebnis war nicht korrekt,
            Ihr neues Ergebnis lautet: """)
            print ""
        print ""
        if task == once:
            print 'Bravo! Das Ergebnis stimmt!\n'
        else:
            print 'Das war leider Falsch!'
            counter += 1

Dann soll unten zum Schluss Counter für die Ausgabe der Fehler benutzt werden.

print 'Sie haben', counter, 'Fehler gemacht'

Ich weiss nicht weiter weil der Counter immer auf null bleibt.

Gruß
Schaf220
Zuletzt geändert von Schaf220 am Dienstag 19. August 2008, 17:55, insgesamt 2-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Poste mal den code eingerückt in entsprechenden Code Tags
Benutzeravatar
Schaf220
User
Beiträge: 113
Registriert: Montag 11. August 2008, 16:00
Wohnort: Bremen
Kontaktdaten:

hab geändert
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

`counter` kann ja nur 0 sein, weil solange ``task != once`` gilt, wird ja die Schleife ausgeführt. Die Überprüfung ``task == once`` nach der Schleife ist somit hinfällig und `counter` sollte eben in der Schleife erhöht werden.

Außerdem sollte ja die Verwendung von ``input`` verzichtet werden, da man damit im Prinzip beliebigen Python-Code ausführen kann.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

global ist böse und hier absolut unnötig. Benutz einfach return um den counter zurückzugeben. Desweiteren sind die Namen nicht sonderlich gut gewählt, counter würde ich z.B. durch fails o.ä. ersetzen.
Das try ist hier völlig unnötig und bewirkt gar nichts.

Schwerwiegender finde ich allerdings die Verwendung von input, vergiss dass es input gibt und verwende int(raw_input("blubb: ")), du musst dann natürlich den ValueError abfangen, der auftreten kann.
Der Grund dafür ist dass input eingegebenen Code ausführt, deswegen bekommst du auch eine Nummer zurück. "import sys; sys.exit()" würde aber gar nichts mehr zurückgeben...

Dein eigentliches Problem ist aber leicht zu lösen. Solange dass Ergebnis falsch ist bleibt man in der while Schleife, in dieser wird der counter nicht erhöht.
Benutzeravatar
Schaf220
User
Beiträge: 113
Registriert: Montag 11. August 2008, 16:00
Wohnort: Bremen
Kontaktdaten:

Vielen Dank
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

DasIch hat geschrieben:"import sys; sys.exit()" würde aber gar nichts mehr zurückgeben...
Echt?

Code: Alles auswählen

>>> eingabe = input()
"import sys; sys.exit()"
>>> print eingabe
import sys; sys.exit()
>>>
Edit: Natürlich müssen die Anführungszeichen weg - aber dann geht es auch nicht.
Natürlich gibt es eine Variante, die funktioniert, aber auf die muss man erstmal kommen ...
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ah, eval wertet import nicht aus und aneinanderhängen geht so auch nicht stimmt. Naja auf __import__("sys").exit() zu kommen ist so schwer jetzt nicht, wenn man weiß was __import__ zurückgibt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

numerix hat geschrieben:Natürlich gibt es eine Variante, die funktioniert, aber auf die muss man erstmal kommen ...
Auf SQL-Injections und XSS-Lücken muss man auch erstmal kommen, aber defensives programmieren wird einem sicherlich nicht schaden. Better safe than sorry und so.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Leonidas hat geschrieben:
numerix hat geschrieben:Natürlich gibt es eine Variante, die funktioniert, aber auf die muss man erstmal kommen ...
Auf SQL-Injections und XSS-Lücken muss man auch erstmal kommen, aber defensives programmieren wird einem sicherlich nicht schaden. Better safe than sorry und so.
Ich wollte auch nicht grundsätzlich für den Einsatz von input() sprechen, aber es gibt so ein paar Statements die hier immer und immer wieder stereotyp wiederholt werden:
global ist böse
input ist böse
* import ist böse

Und das häufig ganz undifferenziert wie ein Echo: Sobald irgendjemand in irgendeinem Programm ein input() stehen hat, braucht man nicht lange warten, bis die Gefährlichkeit betont wird. Das wird der Sache aber nicht in allen Fällen gerecht. Denn in vielen Fällen handelt es sich offensichtlich um Programme aus dem schulischen (Anfangs-)Informatikunterricht, wie in diesem Thread sicher auch. Und da ist es m.W. nach eben nicht unüblich, zu einem relativ frühen Zeitpunkt, wo noch keine Zeichenketten bekannt sind oder gar die Möglichkeiten von Typumwandlungen, zunächst input() eingeführt wird, damit man mit den ersten kleinen Programmen überhaupt interagieren kann. Und in diesem Stadium ist input() nicht "böse" und auch nicht gefährlich. In diesem Stadium der Programmierung einen Ausdruck zusammenzubasteln, der wirklich via input Schaden anrichtet, ist nämlich nicht so einfach - außer man sucht gezielt nach, weil man Schaden anrichten will (und dann findet man auch andere Möglichkeiten).

Gleiches gilt für global:
Wenn ein Pythonkurs nicht direkt über OOP einsteigt, dann sondern zunächst mal ganz ohne OOP grundlegende Datenstrukturen und Algorithmen behandelt, dann gibt es eben gelegentlich Fälle, wo eine Lösung mit global weit weniger aufwändig und transparenter ist als eine ohne.

Und auch für den * import:
Wenn ich weiß, was ich tue, das heißt, ich weiß, was ich da importiere, spricht doch gar nichts dagegen, insbesondere bei Modulen, bei denen auf globaler Ebene nicht viel los ist und der Namensraum eben nicht "zugemüllt" wird.

Nicht, dass ich falsch verstanden werde: Es ist sinnvoll, von input(), global und - meistens - *import wegzukommen, aber es gibt z.B. die o.g. Fälle, wo man der Sache m.E. nicht gerecht wird, wenn man einfach immer ins gleiche Horn bläst.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

@numerix:

Solche Aussagen müssen einfach jedesmal getroffen werden, damit sich die Leute, die in Python irgendwann programmieren wollen, sich keinen schlechten Programmierstil aneignen.

Natürlich gibts hier auch Anfänger, aber soll man ihnen verheimlichen was gut ist und was schlecht ist? Soll man sich denken: "NAJA, er wird zwar später riesige Probleme haben diesen Programmierstil mit global, Sternchenimporte und sowas sich wieder abzueignen, aber hey, wieso ihn drauf ansprechen, is ja voll gemein und so!" ... Nee, so geht das nicht. So hab ich Python nicht gelernt... Und ich denke ich kann's inzwischen zumindest ein wenig.

Zu global: Es hat relativ wenig mit OOP zu tun, viel mehr mit einem gewissen Ablauf und "Magie". Es muss ein Strang durchgeführt werden:

Befehl1 ruft Funktion1 auf, die seine gewissen locals() hat. Diese bekommt sie nur von Argumenten, nicht magisch von außen!

Sonst sucht man irgendwann, wenn man mal mehr als 20 Zeilen Code hat, wie'n Bekloppter: WO wird denn nun die Variable erstellt, wo wird sie definiert. Wenn sie ein Argument ist, was meistens 0 Mehraufwand ist, dann sieht man: AHA, da kommt der Wert her *ein bisschen davor les* ACH SO ist der entstanden. Man hilft den Leuten nur damit!... Auch wenns manchmal ein bisschen fies wirkt, wenn man den Leuten wie Leonidas, BlackJack, Lunar und allen anderen Spezialisten, die nicht minder schlecht sind, sagt: "Hey, das klang aber jetzt irgendwie fies"... Sind sie menschlich und entschuldigen, rechtfertigen sich...

Zu Input: Find input() und raw_input() für mich selbst eh nicht so besonders toll... Ich finde man sollte den Leuten gleich von Anfang an den Umgang mit sys.argv und Kommandozeilenargumenten lehren, damit ist der Umgang auch wesentlich leichter und in einem echten Programm der Nutzen auch wesentlich höher für sowas wie "DEIN NAME IST ALSO SPAM AND EGGS" und so... Aber wenn man's braucht, spricht nichts für input, meiner Meinung nach. Deswegen wird's ja auch bei python3000 umbenannt und raw_input wird zu input. Weil's eben so irreführend ist und man input() wirklich nur in absoluten Ausnahmefällen benötigt.

Zu den *-Importen: Es geht ja absolut nicht nur um den zugemüllten Namensraum, sondern auch um die Übersichtlichkeit. Tkinter-Programme ohne namespaces sind gleich 100mal verwirrender, finde ich. Man muss erstmal schauen: "Oh Gott, wo kommt nur dieser Name nun her?"... Wenn aber ein tk. davor steht weiß man sofort: "AHA, das gehört also zu tkinter *in Dokumentation schau* super =D" Anstatt halt wie'n blöder zu versuchen, die wichtigsten Sachen aus tk auswendig zu lernen und dann aus dem FF zu können... Ich finde sie fördern einen etwas längeren aber auch sauberen Programmierstil. Und ich meine... 3 Zeichen vor den Variablen ist ja kein Weltuntergang, insbesondere wenn man auto-completion hat, oder?...
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

numerix hat geschrieben:Und das häufig ganz undifferenziert wie ein Echo: Sobald irgendjemand in irgendeinem Programm ein input() stehen hat, braucht man nicht lange warten, bis die Gefährlichkeit betont wird. Das wird der Sache aber nicht in allen Fällen gerecht.
Schön, in den meisten Fällen ist das Sicherheitsargument wohl eher schwach. Aber in den meisten Fällen wird `input` auch benutzt, um eben ein ``raw_input()`` zu haben, bei dem am Ende Zahlen rauskommen, meistens in Verbindung mit einer "Sie haben leider etwas Falsches eingegeben"-Meldung. Und dafür eignet sich `input` IMHO kein bisschen und ``int(raw_input())`` wäre der richtige Weg. Da wird ein ValueError geworfen, wenn es keine Zahl ist. Bei `input` hingegen kann man sehr viel eingeben, ohne dass eine Ausnahme geworfen wird. Im konkreten Fall beispielsweise kann der Anwender einfach ``once`` eingeben und er hat immer das Richtige eingegeben. `input` akzeptiert jede Expression, und eben nicht "nur Zahlen", und das geht dann bei der Weiterverarbeitung schief. Meiner Meinung nach sollte dem Programmierer bewusst sein, dass es Typen gibt und dass eine Benutzereingabe nun mal ein String ist, den er umwandeln muss.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@Trundle & BlackVivi:
Überwiegende Zustimmung zu euren Beiträgen. Tkinter mit * import ist in der Tat Mist. Das scheint aber offenbar nicht selten in Schulen gelehrt zu werden, denn sowas erlebt man hier im Forum bei Aufgaben aus dem schulischen Raum leider öfter. Nicht gut.

Der Einsatz von global() hat schon etwas mit OOP zu tun (bzw. damit, dass man OOP nicht verfügbar hat, weil man z.B. im Unterricht - noch - nicht gelernt hat). Während es mit OOP ein Leichtes ist, über Methodengrenzen hinweg Daten austauschen, ist das auf Modulebene ohne Klassen eben nicht möglich. Und es gibt aus meiner Sicht - aber das mag dann eben auch Geschmacksfrage sein - Fälle, wo der Einsatz von global gerechtfertigt ist, weil ansonsten die Anzahl an zu übergebenden Parametern weiter erhöht und das ganze unübersichtlich wird. Bei relativ kurzen Programmen halte ich das für vertretbar; wird der Code länger, wird es ungünstiger.

Ja mit Py3000 ist die Geschichte mit dem input() zu Ende; zumindest fallen diese Art von Diskussion dann weg. Ein "eval(input())" dürfte man auch bei Anfängern wohl eher selten finden ...
Vielleicht liegt der bei Anfängern beliebte Einsatz von input() gegenüber raw_input() auch mit an der unglücklichen Namensgebung: Die suggeriert doch irgendwie: input() ist die normale Eingabefunktion, raw_input() ist ein Sonderfall, irgendwas spezielles, braucht man nicht ... Py3000 räumt da ja auf.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

@numerix
*-Importe haben äußerst selten Vorteile, meiner Meinung nach... Das hat nicht nur mit Tkinter zu tun (da ist übrigens so weit verbreitet weil die ganzen Tkinter-Tutorials das immer als toll und richtig darstellen). Bei einer Konsolensitzung mit numpy wo du gerne mal ein bisschen was ausrechnen magst oder bei einem Skript was vllt 20 Zeilen lang wird... obwohl, selbst da kannst du from module import classes, functions benutzen, was die Übersichtlichkeit fördert...

Ich verstehe den Zusammenhang zwischen OOP und global trotzdem nicht. OOP hat zwar echt viel mit Daten und Kommunikation zwischen Objekten und sowas zu tun, hast du ja richtig angemerkt. Aber trotzdem ändert das nichts daran, dass man auch vernünftig programmieren kann ohne global zu benutzen. Wenn eine Funktion zu viele Argumente hat, kann man sie meistens in weiter Funktionen bzw. nested Functions aufteilen. global (übrigens ohne (), da keine Funktion sondern statement) wird in einer vollkommen normalen Applikation meiner Meinung nach _nie_ gebraucht und sollte auch nur dann benutzt werden, wenn es nichts mit Übersichtlichkeit oder Faulheit zu tun hat, sondern man GENAU weiß, was man tut in dieser Sekunde.... Und sich allen Auswirkungen bewusst ist und alle sonstigen Auswirkungen dagegen abgewogen hat.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Es gibt jetzt nich so viel was ich zu dem Thema beitragen kann, da BlackVivi und Trundle das meiste schon vorweg genommen haben. Aber vielleicht kann ich ja einen frischen Blickpunkt einwerfen.
numerix hat geschrieben:Ich wollte auch nicht grundsätzlich für den Einsatz von input() sprechen, aber es gibt so ein paar Statements die hier immer und immer wieder stereotyp wiederholt werden:
global ist böse
input ist böse
* import ist böse
Richtig, es gibt sogar noch mehr Stereotypen wie OOP ist der Retter aller Softwareprobleme, Java bietet durch das Typsystem inherent mehr Sicherheit, Linux ist das tollste OS für Nerds, Mac OS X das für Designer und Windows das für Idioten. Sowas nennt sich allgemeine Meinung aber wir sehen ja, dass in vielen Fällen das nicht so ist. Wir haben die Erfahrung das etwas differenzierter zu sehen und festzustellen dass einiges von den Stereotypen stimt und anderes hingegen nicht. Du kritisierst dass ``global``, ``input`` und ``*-Import`` auf papageiartige Weise von den Leuten verteufelt werden, weil andere es sagen. Das stimmt, jedoch muss man das etwas differenzierter, vielleicht auch in einem Kontext sehen. Wie wäre es mit der Sichtweise von jemandem der die Sprache kennt und einem Anfänger Tipps gibt auf die der Anfänger letztendlich auch von selbst durch Versuch und Irrtum auch gekommen wäre. Nur ist eben nicht immer die Zeit jedes mal wieder zu erklären warum man etwas nicht so, sondern anders machen sollte. Daher finde ich es sinnvoll wenn so etwas auf einer Wiki-Seite zusammengefasst wird, die man zum Tipp linken kann, damit die Verteufelung von irgendetwas nicht dogmatisch wirkt sondern zeigt, dass da auch wahre technische Gründe dahinter stehen.

Das ``input()`` oft eigentlich fälschlicherweise mit ``raw_input()`` verwechselt wird hat Trundle ja schon erklärt. Für die Stern-Imports gilt, dass sie eigentlich für den Interaktiven Modus gedacht sind, damit man sich das Eintippen von Namespaces spart - mit einem ^D ist der Code aber auch wieder weg und muss von niemandem in Zukunft gelesen werden, dort ist so eine Vereinfachung durchaus mal praktisch.
numerix hat geschrieben:Gleiches gilt für global:
Wenn ein Pythonkurs nicht direkt über OOP einsteigt, dann sondern zunächst mal ganz ohne OOP grundlegende Datenstrukturen und Algorithmen behandelt, dann gibt es eben gelegentlich Fälle, wo eine Lösung mit global weit weniger aufwändig und transparenter ist als eine ohne.
Eigentlich nicht. Denn wenn man nicht OOP nutzt, wird man aber zumindest wohl Funktionen nutzen und die haben in Python wie auch in der Mathematik auch Rückgabewerte wie eben y = x². Die Idee, noch einen globalen Zustand einzuführen macht hingegen das Verständnis schwieriger, da Funktionen nicht mehr wie Blackboxen funktionieren wodurch die Zuordnung von Eingabe -> Funktion -> Ausgabe erschwert wird. Der globale Zustand gibt vielen Menschen ein warmes, flauschiges Gefühl der Geborgenheit (vielleicht weil Menschen an einen Globalen Zustand gewohnt sind), daher gibt es etwa das Singleton oder auch Borg-Pattern die eben globalen Zustand in ein Objekt verpacken und damit auch die OOP-Anhänger glücklich zu machen. Faktisch ist aber, dass globaler Zustand recht kompliziert zu handhaben ist, wie etwa Verwaltung des Zustandes über mehrere Threads beweist. (Hauptsächlich funktionale) Sprachen wie Erlang versuchen auch möglichst den globalen Zustand soweit es geht zu verbannen, weil er eben anfangs nett und weich ist aber später eher zu Verwirrung und Komplikationen führt.
numerix hat geschrieben:Und auch für den * import:
Wenn ich weiß, was ich tue, das heißt, ich weiß, was ich da importiere, spricht doch gar nichts dagegen, insbesondere bei Modulen, bei denen auf globaler Ebene nicht viel los ist und der Namensraum eben nicht "zugemüllt" wird.
Es geht weniger um das zumüllen, das ist nämlich, wie die die Namen mit den zwei Unterstrichen auch beweisen eher ein geringes Problem[1]. Das Problem ist die Übersicht. Manchmal fragt man sich wo ein bweisses Symbol/Name herkommt. Dies ist bei *-Imports eben nicht ganz ersichtlich. Vor allem wenn ich viele verschiedene Module importiere ist es praktisch mehrere Namespaces zu haben, aus denen man die Namen ziehen kann und in denen man dann auch Nachschauen kann.

[1] Die Scheme-Community hatte da unter anderem deswegen weil Scheme ein Lisp-1 mit einem gemeinsamen Namespace für Funktionen, Makros und Variablen ist, da ziemliche Probleme mit Name-Clashes, in denen Namen überschrieben worden sind. Dies führte unter anderem zu den hygienischen Makros, welche ein recht hochtrabender Begriff für ein gar nicht so kompliziertes Konzept ist, was eben sicherstellt dass es möglichst zu keinen Namenskollisionen kommt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Zu den *-Importen: Wenn man mehr als einen davon verwendet, und irgendwann in der Zukunft ein Modul in einer neuen Version einen Namen verwendet, der auch in einem anderen, auf diese Weise importierten Modul verwendet wird, gibt's Probleme. Und Programme sollten nicht plötzlich, ohne das man am Quelltext selber etwas ändert, "kaputt gehen". Das ist sehr überraschend und nervig.

Wenn man sich an *-Importe gewöhnt, steigt die Gefahr für so etwas, da die Namen dann über ganze Importhierarchien verteilt werden. Man holt sich dann nicht nur alle Namen aus einem Modul in den Namensraum, sondern auch alle, die dieses Modul per * importiert hat und so weiter. Damit führt man das Konzept von Modulnamensräumen ad absurdum.

Namenskonflikt, den ich schon ein paar mal in Anfängerquelltext gesehen habe: `PIL.Image` und `Tkinter.Image`. Das ist also kein rein theoretisches Problem.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:Namenskonflikt, den ich schon ein paar mal in Anfängerquelltext gesehen habe: `PIL.Image` und `Tkinter.Image`. Das ist also kein rein theoretisches Problem.
Ja, wobei ich zugeben muss, dass dies aus irgendeinem Grund seit Jahren nicht mehr zur Sprache kam. Also entweder finden Anfänger unsere alten Threads dazu oder sie nutzen Tkinter/PIL nicht mehr.

Aber für Namenskonflikte braucht man gar nicht 3rd-Party Module zu nehmen; wenn man per *-Import ``os`` reinzieht, überschreibt man das Builtin ``open()`` durch ``os.open``.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten