Gewinnspiel

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.
Gary123456
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?
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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?
Benutze doch einfach einen Rückgabewert.

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()
Lass das mal laufen und schau dir das Ergebnis an.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

EDIT: Da stand Unsinn
Gary123456
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()

    
So besser? Ich habe den gleichen Fehler schonmal gemacht, nur nicht daraus gelernt.
anogayales
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.
Gary123456
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 :roll: Und zusätzlich ließ ich zum Dritten Male das Kapitel Funktionen durch. Sorry, das ich etwas länger brauche :)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

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:

Code: Alles auswählen

def parabel(x):
    pustekuchen = 5
    return x**2

# Beispielaufruf
y = parabel(2) # y: 4
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.)
Gary123456
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:

Code: Alles auswählen

if menü_query == "1":
        themen()
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.
BlackJack

@Gary123456: Die Lösung ist es den Schwierigkeitsgrad als Argument an `themen()` zu übergeben.
Sirius3
User
Beiträge: 18299
Registriert: Sonntag 21. Oktober 2012, 17:20

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:

Code: Alles auswählen

schwierigkeit = schwierigkeiten()
thema = themen()
frage = frage_holen(thema, schwierigkeit)
if frage_beantworten(frage):
    print "Richtig beantwortet"
else:
    print "Falsch beantwortet"
Gary123456
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()
    
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Und wo ist da nun die Schwierigkeiten Funktion hin?
Gary123456
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.
Benutzeravatar
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 ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
Es geht nicht um die Länge des Quellcodes sondern um Übersichtlichkeit.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Es geht nicht um die Länge des Quellcodes sondern um Übersichtlichkeit.
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.

Und Danke an euch
Sirius3
User
Beiträge: 18299
Registriert: Sonntag 21. Oktober 2012, 17:20

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.
Gary123456 hat geschrieben:Die einzelnen Themen müssten in Funktionen geschrieben werden
Jedes Thema für sich unterscheidet sich nur im Inhalt, also brauchst Du auch keine einzelnen Funktionen pro Thema sondern nur eine.
nezzcarth
User
Beiträge: 1778
Registriert: Samstag 16. April 2011, 12:47

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.
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.

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).
Benutzeravatar
snafu
User
Beiträge: 6881
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@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. ;)
anogayales
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.
Antworten