Mehrere Funktionen in einer anderen Funktion abrufen.

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
Slim Shady
User
Beiträge: 15
Registriert: Dienstag 15. November 2016, 22:36
Wohnort: Wuppertal

Hallo,

Ich bin gerade dabei, mir mit einem kleinen textbasierten Übungsspielchen Python ein wenig näher zu bringen, und in diesem Textspiel gibt es einen Hauptteil, quasi die "Story", und viele kleine Unterprogramme für den Levelneustart oder für den Spielstart. Wie gesagt, nur ein bisschen Spielerei. Als ich heute aber mein Ladezeiten-Codeschnipsel auch dann einsetzen wollte, wenn man das Spiel zum ersten mal startet, habe ich den Code auch noch in eine Funktion gepackt, damit ich ihn nicht zweimal schreiben muss und leichter verändern kann.

Allerdings funktioniert er nicht so wie er soll, der Codeschnipsel für den Neustart sieht so aus:

Code: Alles auswählen

def reverse(): #Die Neustart-Funktion des Spiels
    userinput = input("Do you want to play again? (y/n):")

    if userinput == "yes":
        load_times()
        Klippe()   #Hier wird das Spiel neu gestartet
        reverse()  

    else:
        print ("Shutting down our hero...")
        sleep(2.5)
        print ("Goodbye")
        exit()

reverse()
Wobei hier Klippe() das eigentliche Spiel ist, auch als Funktion, und reverse() startet die Funktion innerhalb der Funktion noch einmal neu, falls der Spieler danach noch einmal neu starten möchte. Ich kann das bestimmt in eine Klammer machen, aber ich stehe noch ziemlich am Anfang und hätte erstmal keine Idee, wie ich das realisieren könnte.
Hier ist übrigens nochmal der Inhalt der load_times()-Funktion. Nichts spannendes, aber der Vollständigkeit zum Zweck:

Code: Alles auswählen

def load_times():
               print ("Reinitialising the programm...")
               sleep(3)
               print ("Catching the turtle...")
               sleep(1)
               print ("Deleting System32...")
               sleep(2)
               print ("Programm successfully loaded...")
               sleep(1)

load_times()
Wenn ich das Programm ausführe, klappt alles wunderbar, aber wenn ich (bzw das Programm) in der Neustart-Funktion die Ladezeitenfunktion abrufe, dann gibt er mir die Fehlermeldung aus, das load_times() nicht definiert sei.

Kann ich nicht mehrere Funktionen hintereinander in einem Programm abrufen?
Oder woran hakelt es?

Über Hilfe wäre ich sehr dankbar.
:!: :?:
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

Das ist schwer zu sagen, ohne den gesamten Quelltext zu sehen. Die Fehlermeldung deutet darauf hin, dass du die beiden Funktionen (ohne Rückgabewerte sind sie das meiner Ansicht nach eigentlich nicht) nicht auf Modulebene definiert, sondern irgendwo "verbuddelt" hast. An sich kannst du natürlich Funktionen aus anderen Funktionen aufrufen -- auch mehrere. Aber diese müssen für einander sichbar sein.

Weiterhin ist der rekursive Aufruft von reverse nicht so gut, weil Python ein Rekursionstiefenlimit hat; irgendwann ist's vorbei mit spielen :)

So in der Richtung sollte es funktionieren:

Code: Alles auswählen

#!/usr/bin/env python3
import time 


def klippe():
    print('Klippe.')


def load_times():
    print ("Reinitialising the programm...")
    time.sleep(3)
    print ("Catching the turtle...")
    time.sleep(1)
    print ("Deleting System32...")
    time.sleep(2)
    print ("Programm successfully loaded...")
    time.sleep(1)


def main():
    while True:
        load_times()
        klippe()
        answer = input('Again? (y/n) ')
        if answer.lower() not in {'y', 'yes'}:
            break


if __name__ == '__main__':
    main()
Slim Shady
User
Beiträge: 15
Registriert: Dienstag 15. November 2016, 22:36
Wohnort: Wuppertal

Hmm, so sieht er tatsächlch besser aus, allerdings drängen sich mir da noch einige Fragen auf:

Was ist ein Rekursionstiefenlimit? Heißt das, Funktionsaufrufe endlich?

Und was bedeutet der letzte Codeschnipsel von Zeile 26 und 27, den kenne ich zwar schon, weiß aber nicht was er macht oder wie man ihn einsetzt.
:!: :?:
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Slim Shady: Rekursion ist, wenn sich eine Funktion immer wieder selbst aufruft. Passiert das sehr oft, kann das ein Fehler sein. Daher gibt es eine Limit um solche Fehler einfacher entdecken zu können. So wie Du Rekursion eingesetzt hast, würde ich das auch als Fehler ansehen. Die if-Zeile in der vorletzten Zeile sorgt dafür, dass main nur aufgerufen wird, wenn die Python-Datei als Programm aufgerufen wird und nicht, wenn die selbe Datei als Modul importiert wird. Damit kann man solchen Code flexibler einsetzten und Tests durchführen.
Slim Shady
User
Beiträge: 15
Registriert: Dienstag 15. November 2016, 22:36
Wohnort: Wuppertal

Hm, ok, aber warum hast du hinter deine Funktionen nicht das hier gemacht:

Code: Alles auswählen

def main():
	print ("main")

main()
Was ich meine, warum fehlt das main() am Ende der Funktion? Braucht man das nicht?
:!: :?:
BlackJack

@Slim Shady: Es steht doch ein ``if __name__ == '__main__': main()`` da. Oder fragst Du etwa warum nicht hinter *jeder* Funktion nicht auch gleich ein Aufruf der gerade definierten Funktion steht? Dann würde ich sagen haben wir Dein Problem gefunden, und ich würde Dich fragen wozu das gut sein sollte.
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Slim Shady hat geschrieben:Code in eine Funktion gepackt, damit ich ihn nicht zweimal schreiben muss und leichter verändern kann.
Hallo Slim Shady, willkommen bei Python und im Forum! Grundsätzlich ist das die richtige Idee und alle machen das so, weil es genau diese Vorteile bietet. Vielleicht schaust du dir mal ein paar andere Programme hier im Forum an, wie es im Detail abläuft. Es sollte eigentlich keine Probleme geben, eine Funktion mehrfach aufzurufen. Diese sollte dann aber auch irgendwann enden und wieder zurückspringen, ggf. gibt sie dabei einige Werte zurück, und sollte jedenfalls nicht ihrerseits immer weiter neue Funktionen aufrufen.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
BlackJack

@Slim Shady: Noch als Tipp um sich zu ”zwingen” das mit den Funktionen richtig zu machen: Verzichte auf die `exit()`- bzw. `sys.exit()`-Funktion. Die sollte man nur verwenden wenn man etwas anderes als 0 als Rückgabecode des Programms an das Betriebssystem übergeben will. Und dann auch nur ganz weit oben in der Aufrufhierarchie. Eigentlich nur direkt im ersten und einzigen Aufruf der Hauptfunktion des Programms; maximal eine Aufrufebene tiefer.

Wenn man nicht explizit etwas an den Aufrufer des Programms zurückgeben will über den Rückgabecode, dann kann man den Kontrollfluss im Programm immer so strukturieren, dass der das ”natürliche Ende” erreicht. Also so, dass einfach kein Code mehr zum Ausführen da ist und das Programm deshalb beendet wird. Ein `exit()`-Aufruf ist in so einem Fall IMHO eine unsaubere Abkürzung, weil man dann auf Kosten der Nachvollziehbarkeit Nachdenken und Strukturieren des Codes weggelassen hat. Zudem macht `exit()` Funktionen schlecht testbar und wiederverwendbar und nimmt dem Aufrufer die Möglichkeit anders zu reagieren als mit einem kompletten Programmabbruch.
Slim Shady
User
Beiträge: 15
Registriert: Dienstag 15. November 2016, 22:36
Wohnort: Wuppertal

Ok, ich habe eure Lösungsansätze mal ausprobiert, und es funktionert auch alles, allerdings fragt er jetzt nicht mehr nach, ob man das Spiel auch wirklich neustarten will. Und ich kriege das Problem irgendwie nicht in den Griff. Kann mir da jemand von euch helfen. Muss ich das in die Iteration integrieren?
:!: :?:
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

Hallo Slim Shady,

Deine Fragen gehen doch sehr auf deine Implementation zurück, die wir nicht kennen.

Wenn du denkst das du in Zukunft öfters Dinge entwickeln und deine Arbeit mit anderen Personen Teilen willst solltest du darüber nachdenken dir einen Github Account zuzulegen.
Auf Github kannst du dir für deine Projekte Repositories anlegen die du dann mit dem ganzen I-Net oder auserwählten Usern auf Github teilen kannst.
Allerdings müsstest du das Versionkonrolltool 'git' beherrschen (leider eine Art von Kryptowissenschaft), ein Tool was in jedem Entwicklungsprojekt mit mehr als einer Person heutzutage Benutzt wird. Früher haben nur ambinionierte Projekte ein öffentliches Interface zu ihrer Codeverwaltung zugelegt, aber heutzutage ist Github unter Informatiker so üblich wie WhatsApp bei Mittelstufeschülern.

Der Vorteil liegt klar auf der Hand: Wir könnten deinen Code über Github einsehen und dir sogar Patches einstellen die du dann verwenden oder zurückweisen kannst.

Guß pyHoax
Antworten