Gewinnspiel
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Warum? Ich will global verwenden, um die lokale Variable auch im globalen Namensraum verwenden zu können. Warum geht das jetzt nicht?
Benutze doch einfach einen Rückgabewert.Gary123456 hat geschrieben:Warum? Ich will global verwenden, um die lokale Variable auch im globalen Namensraum verwenden zu können. Warum geht das jetzt nicht?
Du verwendest Funktionen noch nicht dafür wofür sie gedacht sind. Sie sollen Code kapseln und nicht einfach Sprungmarken sein. Ein gutes Beispiel dafür ist der Aufruf von schwierigkeiten in main und anschließend der Aufruf von main in schwierigkeiten. Du erzeugst dort eine astreine Rekursion ohne Abbruchbedingung.
Auf das Wesentliche reduziert sieht der Code so aus:
Code: Alles auswählen
def schwierigkeiten():
main()
def main():
schwierigkeiten()
main()
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
EDIT: Da stand Unsinn
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Code: Alles auswählen
def schwierigkeiten():
schwierig_list = ["[1] leicht", "[2] mittel", "[3] schwierig"]
for i in schwierig_list:
print(i)
schwierig_query = input("Geben Sie die gewünschte Schwierigkeit ein!")
return schwierig_query
def themen():
themen_list = ["[1] Fussball", "[2] Schauspieler", "[3] Musik", "[4] Filme", "[5] Technik", "[6] Wirtschaft", "[7] Internet", "[8] Handy", "[9] Spiele","[10] Schule"]
try:
print("Ausgewählte Schwierigkeit:",schwierig_query)
except:
print("Keine Schwierigkeit ausgewählt!")
return
for i in themen_list:
print(i)
themen_query = input("Geben Sie die gewünschte Option ein!")
return themen_query
def main():
menü = ["[1] Themen", "[2] Schwierigkeiten", "[3] Exit"]
for i in menü:
print(i)
menü_query = input("Geben Sie nun die gewünschte Option ein!")
if menü_query == "1":
themen()
elif menü_query == "2":
schwierigkeiten()
main()
elif menü_query == "3":
return
else:
print("Sie haben was falsches eingegeben!")
if __name__ == "__main__":
main()
-
- User
- Beiträge: 456
- Registriert: Mittwoch 15. April 2009, 14:11
Du machst halt immer noch nix mit dem Rückgabewert vom schwierigkeiten() Aufruf.
Und wegen der Nummerierung, was machst du wenn du [4] Filme vor [2] Schauspieler haben willst. Dann musst du von Hand die Indizes anpassen. Ist halt mehr Arbeit für dich, enumerate ist da viel angenehmer.
Und wegen der Nummerierung, was machst du wenn du [4] Filme vor [2] Schauspieler haben willst. Dann musst du von Hand die Indizes anpassen. Ist halt mehr Arbeit für dich, enumerate ist da viel angenehmer.
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Ich denke ich schlaf mal lieber. Bei mir kommt nix mehr in den Kopf. Ich denke ich mach wieder mal ganz typische Gary-Fehler
Und zusätzlich ließ ich zum Dritten Male das Kapitel Funktionen durch. Sorry, das ich etwas länger brauche 


Der "richtige" und der "falsche" Gebrauch von Funktionen ist sicher nicht gleich einleuchtend als Anfänger. Auch der Begriff Sprungmarke sagt Dir sicher nichts. Also was sind Funktionen? Mathematisch sind Funktionen Abbildungen. Das leuchtet ein, wenn Du Dir mal die Funktion f(x) --> x² anschaust, hier wird jedem x x² zugeordnet, was eine Parabel beschreibt. Das kannst Du direkt aufs Programmieren übertragen, z.B:Auch hier gilt, dass jedem x ein x² zugeordnet wird. Das x wird dabei als Parameter der Funktion mitgegeben, das x² ist der Rückgabewert.
An diesem simplen Beispiel kann man jetzt mehrere Dinge herleiten, wofür Funktionen in der Programmierung genutzt werden (sollten). Zunächst ist der Funktionsaufruf ein Sprung (und damit die Funktion eine Sprungmarke). Einen Sprung hast Du immer dann, wenn Du den Code nicht einfach weiterlesen kannst, sondern springen musst, um das Programm nachvollziehen zu können. Der Rechner macht in der Regel da nichts anderes, er springt in die Funktion, führt den Code aus und springt zur Aufrufstelle zurück. Aus der Funktion nimmt er den Rückgabewert nimmt, den Du mit return setzen kannst. Alles andere ist verloren (Aufrufender kennt pustekuchen nicht). Der Aufrufer sieht nur das Ergebnis, d.h. der Aufruf von parabel(2) wird zum Ergebnis 4 ersetzt. Das ist ein wichtiger Punkt, den Aufrufenden interessiert es nicht, wie parabel(x) zum Ergebnis kommt, er vertraut darauf, dass die Funktion tut, was sie verspricht. Der Wert ist für den Aufrufenden quasi magisch da.
Die Sichtweise, dass Funktionen "Boxen" sind, in die ich was reinschmeissen kann und was rauskommt, hilft ungemein bei der Strukturierung von Programmen. Dabei sollte man den Programmfluss vom Aufrufenden aus denken und den Funktionsaufruf nur als *vorübergehenden* Exkurs verstehen, heisst, das Programm sollte hinter dem Funktionsaufruf weiterlesbar sein und nicht in sich selbstaufrufenden Funktionen verlorengehen (solche Rekursionen sind bei manchen Teilproblemen erwünscht, taugen aber definitiv nicht zur Hauptprogrammsteuerung).
Zum Punkt - alles andere ist verloren: Mit global kann man in Python dieses Verhalten aushebeln. Das ist aber *böse böse böse*. Warum? Es verletzt ganz klar die Boxidee. Der Funktionsaufruf führt zur Manipulation äußerer Zustände, womit die Box nicht mehr unabhängig ist. Damit handelt man sich ganz schnell unwartbaren Code ein, welcher z.B. mit einer kleinen Umstellung tolle Nebeneffekte provoziert. In der Regel ist der Gebrauch von global ein Hinweis auf ein falsches Progammdesign.
Keine Ahnung, ob Dir dieser Erklärungsversuch weiterhilft. Ich habe versucht, es einfach zu halten (Dinge wie Prozedur vs. Funktion, imperative vs. funktionale Programmierung, Thread- und Wiedereintrittssicherheit habe ich bewusst rausgehalten.)
Code: Alles auswählen
def parabel(x):
pustekuchen = 5
return x**2
# Beispielaufruf
y = parabel(2) # y: 4
An diesem simplen Beispiel kann man jetzt mehrere Dinge herleiten, wofür Funktionen in der Programmierung genutzt werden (sollten). Zunächst ist der Funktionsaufruf ein Sprung (und damit die Funktion eine Sprungmarke). Einen Sprung hast Du immer dann, wenn Du den Code nicht einfach weiterlesen kannst, sondern springen musst, um das Programm nachvollziehen zu können. Der Rechner macht in der Regel da nichts anderes, er springt in die Funktion, führt den Code aus und springt zur Aufrufstelle zurück. Aus der Funktion nimmt er den Rückgabewert nimmt, den Du mit return setzen kannst. Alles andere ist verloren (Aufrufender kennt pustekuchen nicht). Der Aufrufer sieht nur das Ergebnis, d.h. der Aufruf von parabel(2) wird zum Ergebnis 4 ersetzt. Das ist ein wichtiger Punkt, den Aufrufenden interessiert es nicht, wie parabel(x) zum Ergebnis kommt, er vertraut darauf, dass die Funktion tut, was sie verspricht. Der Wert ist für den Aufrufenden quasi magisch da.
Die Sichtweise, dass Funktionen "Boxen" sind, in die ich was reinschmeissen kann und was rauskommt, hilft ungemein bei der Strukturierung von Programmen. Dabei sollte man den Programmfluss vom Aufrufenden aus denken und den Funktionsaufruf nur als *vorübergehenden* Exkurs verstehen, heisst, das Programm sollte hinter dem Funktionsaufruf weiterlesbar sein und nicht in sich selbstaufrufenden Funktionen verlorengehen (solche Rekursionen sind bei manchen Teilproblemen erwünscht, taugen aber definitiv nicht zur Hauptprogrammsteuerung).
Zum Punkt - alles andere ist verloren: Mit global kann man in Python dieses Verhalten aushebeln. Das ist aber *böse böse böse*. Warum? Es verletzt ganz klar die Boxidee. Der Funktionsaufruf führt zur Manipulation äußerer Zustände, womit die Box nicht mehr unabhängig ist. Damit handelt man sich ganz schnell unwartbaren Code ein, welcher z.B. mit einer kleinen Umstellung tolle Nebeneffekte provoziert. In der Regel ist der Gebrauch von global ein Hinweis auf ein falsches Progammdesign.
Keine Ahnung, ob Dir dieser Erklärungsversuch weiterhilft. Ich habe versucht, es einfach zu halten (Dinge wie Prozedur vs. Funktion, imperative vs. funktionale Programmierung, Thread- und Wiedereintrittssicherheit habe ich bewusst rausgehalten.)
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Hi,
verstehe schon was Du meinst, nur mein Problem ist, dass ich unter Themen nicht die Schwierigkeit anzeigen kann, weil sie nur in der Funktion (also lokal) verwendet wurde. Ich müsste praktisch nochmals Schwierigkeiten aufrufen, um diesen Fehler zu umgehen.
Ich meine zu diesem Bereich:
In der Funktion Schwierigkeit wird festgelegt, welche Schwierigkeit verwendet werden soll. Nun rufe ich diese Funktion in der anderen Bedingung ab und nicht ind dieser 1.. Der Rückgabewert dieser Funktion ist ja der Schwierigkeitsgrad (1 = leicht; 2 = mittel; 3 = schwer). Nun - dieser Rückgabewert ist ja logischerweise in der ersten Bedingung nicht gefragt.
Ich sehe drei Möglichkeiten, wie man das lösen könnte:
1. Diese Variable global machen (=> Unsinn)
2. Teil des Programmes ohne Funktionen schreiben ( => könnte funktionieren)
3. Den Menüpunkt "[2] Schwierigkeiten" weglassen und diese im Menüpunkt "[1] Themen" unterbringen ( => Würde funktionieren)
Kennt jemand andere Lösungen? Denn ich find diese etwas unschön.
verstehe schon was Du meinst, nur mein Problem ist, dass ich unter Themen nicht die Schwierigkeit anzeigen kann, weil sie nur in der Funktion (also lokal) verwendet wurde. Ich müsste praktisch nochmals Schwierigkeiten aufrufen, um diesen Fehler zu umgehen.
Ich meine zu diesem Bereich:
Code: Alles auswählen
if menü_query == "1":
themen()
Ich sehe drei Möglichkeiten, wie man das lösen könnte:
1. Diese Variable global machen (=> Unsinn)
2. Teil des Programmes ohne Funktionen schreiben ( => könnte funktionieren)
3. Den Menüpunkt "[2] Schwierigkeiten" weglassen und diese im Menüpunkt "[1] Themen" unterbringen ( => Würde funktionieren)
Kennt jemand andere Lösungen? Denn ich find diese etwas unschön.
@Gary123456: Die Lösung ist es den Schwierigkeitsgrad als Argument an `themen()` zu übergeben.
Hi Gary123456,
Die Funktion von Funktionen sollten immer so ausgewählt werden, dass es möglichst wenig Querabhängigkeiten gibt.
In Deinem Fall: Wieso muss die Themenauswahl-Funktion von der Schwierigkeit wissen?
Die Funktion »frage_holen« muss sowohl Thema als auch Schwierigkeit wissen, also bekommt sie diese zwei Angaben als Argumente übergeben.
In etwa so:
Die Funktion von Funktionen sollten immer so ausgewählt werden, dass es möglichst wenig Querabhängigkeiten gibt.
In Deinem Fall: Wieso muss die Themenauswahl-Funktion von der Schwierigkeit wissen?
Die Funktion »frage_holen« muss sowohl Thema als auch Schwierigkeit wissen, also bekommt sie diese zwei Angaben als Argumente übergeben.
In etwa so:
Code: Alles auswählen
schwierigkeit = schwierigkeiten()
thema = themen()
frage = frage_holen(thema, schwierigkeit)
if frage_beantworten(frage):
print "Richtig beantwortet"
else:
print "Falsch beantwortet"
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Ich denke so ist es am Leichtesten und am sinnvollsten:
Code: Alles auswählen
def main():
menü_liste = ["[1] Themen", "[2] Exit"]
themen_liste = ["[1] Fussball", "[2] Schauspieler", "[3] Musik", "[4] Filme", "[5] Technik", "[6] Wirtschaft", "[7] Internet", "[8] Handy", "[9] Spiele", "[10] Schule"]
for i in menü_liste:
print(i)
menü_query = input("Geben Sie die gewünschte Option ein!")
if menü_query == "1":
schwierig_query = input("Geben Sie die gewünschte Schwierigkeit ein! 1 für leicht, 3 für schwer!")
for i in themen_liste:
print(i)
print("Gewählte Schwierigkeit: ", schwierig_query)
if __name__ == "__main__":
main()
-
- User
- Beiträge: 456
- Registriert: Mittwoch 15. April 2009, 14:11
Und wo ist da nun die Schwierigkeiten Funktion hin?
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Naja ich denke, mit Funktionen wäre alles viel schwieriger. So funkt es ohne Probleme. Mit Funktionen wäre der Quellcode auch etwas länger.
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Wegen des Spiels an sich werfe ich mal folgendes ein: https://github.com/Lysander/snippets/tr ... simplequiz
Wegen dieser statischen Menü-Entwürfe:
http://wiki.python-forum.de/Tutorial_Textmen%C3%BC
SCNR
Wegen dieser statischen Menü-Entwürfe:
http://wiki.python-forum.de/Tutorial_Textmen%C3%BC
SCNR

encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
Es geht nicht um die Länge des Quellcodes sondern um Übersichtlichkeit.Gary123456 hat geschrieben:Naja ich denke, mit Funktionen wäre alles viel schwieriger. So funkt es ohne Probleme. Mit Funktionen wäre der Quellcode auch etwas länger.
-
- User
- Beiträge: 318
- Registriert: Dienstag 26. Februar 2013, 18:39
Es kommt darauf an, wie, wo , wann man welche Funktionen einsetzt. Für mich bleibt der Code übersichtlich. Die einzelnen Themen müssten in Funktionen geschrieben werden, aber ganz leichte Listen, die dann mit for wieder ausgegeben werden, denke ich eben nicht.Es geht nicht um die Länge des Quellcodes sondern um Übersichtlichkeit.
Und Danke an euch
Man entscheidet sich für eine Funktion, wenn die Funktionalität eines Stückchen Codes isoliert für sich etwas sinnvolles ist. Also z.B. eine Liste ausgeben und den User nach einer Zahl fragen. Diese Funktion braucht eine Liste mit Optionen und gibt eine Zahl zurück. Das heißt also, die Funktionalität ist in sich abgeschlossen und kann eventuell an vielen Stellen wieder gebraucht werden (Abfrage des Themas, der Schwierigkeit, usw.) und für sich einzeln auch getestet werden. Und hier hilft es Dir auch wieder, wenn die Funktion in Deinen Augen zu einfach ist, denn einfache Funktionen lassen sich auch einfach testen und das ist gut.
Jedes Thema für sich unterscheidet sich nur im Inhalt, also brauchst Du auch keine einzelnen Funktionen pro Thema sondern nur eine.Gary123456 hat geschrieben:Die einzelnen Themen müssten in Funktionen geschrieben werden
Du hast in den ganzen Themen, die du bisher hier eröffnest hast doch schon zahlreiche wertvolle Tipps von Leuten, die wissen, was sie sagen, bekommen. Wenn ich mir allerdings die Codes, die du postest so ansehe (resp. in diesen Fall deinen Plan) kann ich darin nicht viel davon erkennen. Das sieht an vielen Stellen so aus, als hättest du grundlegende Aspekte der Strukturierung von Programmen noch nicht ausreichend verstanden.Gary123456 hat geschrieben: Es kommt darauf an, wie, wo , wann man welche Funktionen einsetzt. Für mich bleibt der Code übersichtlich. Die einzelnen Themen müssten in Funktionen geschrieben werden, aber ganz leichte Listen, die dann mit for wieder ausgegeben werden, denke ich eben nicht.
Das ist nicht weiter schlimm, aber statt rumzudiskutieren, warum man das deiner Meinung nach doch so machen kann, solltest du vielleicht die Ratschläge, die du bekommst beherzigen - und dir evtl. mal den Code, den andere so produzieren ansehen, um davon zu lernen (und ggf. zu einer vernünftigen Einschätzung der eigenen Fähigkeiten zu gelangen).
@Gary123456: Wenn wir mal bei deinem zuletzt gezeigten Code bleiben, dann hast du durchaus Recht, dass z.B. die 2 Zeilen für das Durchlaufen der Menüelemente zwecks Bildschirmausgabe keine eigene Funktion benötigen bzw dass dies hier nicht wirklich sinnvoll wäre. Wohl aber kann man eine Funktion namens `erfrage_thema()` und weitere Funktion namens `erfrage_schwierigkeit()` schreiben, die jeweils die Benutzerauswahl in Form einer Zahl zurückgeben und sich um die entsprechend vorgelagerte Textausgabe für die Auswahlpunkte und Abfrage kümmern. Oder spricht in deinen Augen etwas gegen dieses Vorgehen?
Übrigens, neben der bereits genannten Test- und Wiederverwendbarkeit kommt noch ein weiterer Vorteil hinzu, wenn man Funktionen für Teilaufgaben implementiert - nämlich die Lesbarkeit. Denn wenn man innerhalb von `main()` ein paar Funktionsnamen wie die oben genannten sieht, dann hat man bereits eine gewisse Vorstellung davon, wie der grobe Ablauf deines Programms ist. Man sieht vor allem auch viel besser, wo ein Arbeitschritt beginnt und wo er aufhört - nämlich jeweils am Anfang bzw am Ende der jeweiligen Funktion. Auch kann man sich als Leser quasi aussuchen, ob man die Details zur Umsetzung eines Abeitsschrittes überhaupt nachsehen möchte (mittels Blick in den zugehörigen Funktionscode) oder ob man sich lieber den darauf folgenden Schritt anguckt. Zuletzt sei noch die Übersichtlichkeit genannt, die im Quelltext besteht, wenn eine Art "Oberfunktion" ihre Hilfsfunktionen in der-und-der Reihenfolge aufruft und man eben nicht alle möglichen Details an einer Stelle "hingeklatscht" lesen muss.
Vielleicht fehlt dir momentan ja noch die Erfahrung, um einzusehen, dass gute Programme voll von Funktionen (oder natürlich Klassen mit Methoden) sind und mitunter eine Vielzahl davon bloß ein einziges Mal aufgerufen wird und dass dieser Umstand auch überhaupt nicht schlimm ist. Du kannst uns aber auf jeden Fall glauben, dass eine Aufteilung von Codeabschnitten sowohl für dich bei der Programmentwicklung als auch für einen Dritten, der am Programmcode interessiert ist und vielleicht sogar Verbesserungen beisteuern möchte, langfristig die bessere Wahl sein wird. Ich kann nachvollziehen, dass du bestimmt in erster Linie schon froh bist, wenn dein Programm tut, was es soll. Aber je komplexer dein Programm wird, desto mehr Schwierigkeiten handelst du dir ein, wenn du nicht frühzeitig den Code in sinnvolle Häppchen aufteilst. Tust du dies nicht, dann prophezeihe ich dir, dass irgendwann der Punkt kommen wird, an dem du den kompletten Code wegschmeissen und alles nochmal neu schreiben musst. Aber gut, spätestens dann hast du die Erfahrung selber gemacht und (hoffentlich) etwas dabei für die Zukunft gelernt.
Übrigens, neben der bereits genannten Test- und Wiederverwendbarkeit kommt noch ein weiterer Vorteil hinzu, wenn man Funktionen für Teilaufgaben implementiert - nämlich die Lesbarkeit. Denn wenn man innerhalb von `main()` ein paar Funktionsnamen wie die oben genannten sieht, dann hat man bereits eine gewisse Vorstellung davon, wie der grobe Ablauf deines Programms ist. Man sieht vor allem auch viel besser, wo ein Arbeitschritt beginnt und wo er aufhört - nämlich jeweils am Anfang bzw am Ende der jeweiligen Funktion. Auch kann man sich als Leser quasi aussuchen, ob man die Details zur Umsetzung eines Abeitsschrittes überhaupt nachsehen möchte (mittels Blick in den zugehörigen Funktionscode) oder ob man sich lieber den darauf folgenden Schritt anguckt. Zuletzt sei noch die Übersichtlichkeit genannt, die im Quelltext besteht, wenn eine Art "Oberfunktion" ihre Hilfsfunktionen in der-und-der Reihenfolge aufruft und man eben nicht alle möglichen Details an einer Stelle "hingeklatscht" lesen muss.
Vielleicht fehlt dir momentan ja noch die Erfahrung, um einzusehen, dass gute Programme voll von Funktionen (oder natürlich Klassen mit Methoden) sind und mitunter eine Vielzahl davon bloß ein einziges Mal aufgerufen wird und dass dieser Umstand auch überhaupt nicht schlimm ist. Du kannst uns aber auf jeden Fall glauben, dass eine Aufteilung von Codeabschnitten sowohl für dich bei der Programmentwicklung als auch für einen Dritten, der am Programmcode interessiert ist und vielleicht sogar Verbesserungen beisteuern möchte, langfristig die bessere Wahl sein wird. Ich kann nachvollziehen, dass du bestimmt in erster Linie schon froh bist, wenn dein Programm tut, was es soll. Aber je komplexer dein Programm wird, desto mehr Schwierigkeiten handelst du dir ein, wenn du nicht frühzeitig den Code in sinnvolle Häppchen aufteilst. Tust du dies nicht, dann prophezeihe ich dir, dass irgendwann der Punkt kommen wird, an dem du den kompletten Code wegschmeissen und alles nochmal neu schreiben musst. Aber gut, spätestens dann hast du die Erfahrung selber gemacht und (hoffentlich) etwas dabei für die Zukunft gelernt.

-
- User
- Beiträge: 456
- Registriert: Mittwoch 15. April 2009, 14:11
Noch ein kleiner Tipp der dir auf langer Sicht vielleicht helfen könnte:
Fang einfach mal an zu programmieren, und denk nicht darüber nach alles perfekt machen zu müssen. Mach Fehler und lerne aus diesen. Ich empfehle dir einfach mal loszulegen, mit der Zeit wirst du selbst merken, dass manche Sachen viel zu kompliziert gelöst sind und nach einer Weile bekommst du selbst ein Gespür dafür was gut und was schlecht ist.
Fang einfach mal an zu programmieren, und denk nicht darüber nach alles perfekt machen zu müssen. Mach Fehler und lerne aus diesen. Ich empfehle dir einfach mal loszulegen, mit der Zeit wirst du selbst merken, dass manche Sachen viel zu kompliziert gelöst sind und nach einer Weile bekommst du selbst ein Gespür dafür was gut und was schlecht ist.