Vorher deklarierte Variable wird in Funktion nicht erkannt

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo zusammen,

    momentan versuche ich mein Wecker-Skript etwas zu verbessern, indem ich der Ausschalt-Taste weitere Funktionen hinzufüge. Siehe Skript:

    Ich bin mir sicher, dass die Experten unter euch viele Verbesserungsmöglichkeiten finden, aber mein Anliegen ist das folgende:

    Starte ich das Skript und drücke dann den Knopf (GPIO18), dann kommt diese Fehlermeldung:

    Code
    UnboundLocalError: local variable 'modus' referenced before assignment

    Jetzt stellt sich mir die Frage, warum diese Fehlermeldung kommt, denn der Modus wurde ja noch vor der Definition des Skriptes definiert.

    Vielen Dank im Voraus,

    Michael :)

  • Vorher deklarierte Variable wird in Funktion nicht erkannt? Schau mal ob du hier fündig wirst!

  • Hallo,

    In Deinem Beitrag sollte enthalten sein:


    Die ungekürzte und vollständige Ausgabe der auftretenden Fehlermeldung (falls vorhanden)


    Nein, deine Variable ist in der Funktion nicht gültig.

    Es würde zwar globale Variablen geben, das müsste aber definiert werden und ist unsauber. Am besten vergisst du den Satz gleich wieder.

    Besser ist es, wenn du Werte, welche außerhalb einer Funktion existieren und in einer Funktion behandelt werden müssen als Parameter an die Funktion übergibst. Benötigt du das Ergebnis der Funktion auch wieder außerhalb, so wird mit return der Wert zurückgegeben.

    Übrigends, auf globaler Ebene werden nur Konstanten definiert. Keine Variablen

    Weiteres zum Gültigkeitsbereich von Variablen siehe hier:

    Python Scope

  • Hallo,

    das Problem dass du auch hast, ist dass du den Zustand des Weckers irgendwo "speichern" musst. Wenn ich das richtig gesehen habe, kannst du hier keine Rückgaben von Funktionen entgegen nehmen, die mit dem Button-Callback ausgeführt werden.

    Dass macht das Ganze etwas komplizierter. Wenn man Zustände speichern will benötigt man meist Klassen. Ich hoffe dass es noch eine andere Möglichkeit gibt, denn das wäre jetzt mit viel Fleißarbeit für dich verbunden. Vielleicht haben die Experten dazu noch eine andere Idee.

    Zu deinem Code, als Ergänzung zu Hofei 's Beitrag: Kommentare werden in Python nicht mit '//' eingeleitet sondern mit einer '#'. Statusabfragen macht man nicht mit '1' und '2' sondern mit 'True' und 'False'. Dann genügt auch ein if modus: oder ein if not modus:

    Wenn du eine Datei öffnen willst, dann musst du sie auch wieder schließen. Am besten du öffnest sie mit dem 'with'-Statement, das schließt die Datei automatisch wieder, auch wenn das Programm abbricht.

    Ich würde die Datei einmal öffnen und den Inhalt an einen Namen binden, anstatt alle 20 Sekunden den gleichen Inhalt aus einer Datei auszulesen.

    Dann wie schon erwähnt stehr auf Modulebene, also die Zeilen ohne Einrückung, nur Code Der Konstanten, Funktionen oder Klassen beschreibt.

    Üblicherweise hat Python eine Funktion mit dem Namen 'main', daraus wird das Programm gesteuert, Funktionen aufgerufen, Argumente übergeben und Rückgaben entgegen genommen.Wenn du weiterhin Interesse hast, dass umzusetzen kannst du dir mal dieses Tutorial durcharbeiten.

    Wie erwähnt habe ich spontan leider kein Beispiel parat, dass nur eine kleine Änderung an deinem Programm mit sich bringen würde.

    Dennoch mal meine Idee wie ich das umsetzen würde, ungetestet:

    Grüße

    Dennis

    Edit: Zur Erklärung wieso das jetzt hier so "umständlich" gemacht wird. Normal kannst du eine Funktion aufrufen, übergibst ihr Argumente, das in den Klammern, und die Funktion gibt etwas zurück, return. Das Was zurück kommt kannst du an einen Namen binden. Das ist der Name vor dem Funktionsaufruf. Das ist normalerweise dann etwas wie das hier:

    Zur Erklärung, 'choice' gibt einfach zufälligerweise 'eins' oder 'zwei' zurück. Das habe ich nur eingebaut, dass die Ausgabe nicht ganz so offensichtlich ist, sondern dass die Funktion auch was macht.

    Diese Vorgehensweise geht bei dir meiner Meinung nach nicht, weil du keinen regualären FUnktionsaufruf wie hier hast. Du sagst nur, welche Funktion beim drücken des Buttons aufgerufen werden soll. Da kann keine Rückgabe entgegen genommen werden. Falls das jemand besser weis, wäre ich auch interessiert.

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

    Einmal editiert, zuletzt von Dennis89 (20. August 2021 um 12:46)

  • Hallo ihr beiden,

    danke erstmal für eure ausführlichen Beiträge :danke_ATDE:

    Ich war beim Verfassen etwas in Eile, weswegen ich den Fehler mit den Kommentaren gemacht und die Fehlermeldung nicht vollständig gepostet habe. Apropos:

    Nun zu euren Beiträgen:

    Es würde zwar globale Variablen geben

    Ich habe es auch mit

    global modus

    modus = 1

    probiert, hat aber nicht funktioniert.

    Wenn man Zustände speichern will benötigt man meist Klassen

    Die Klassen- bzw. Objektprogrammierung in Python ist finde ich etwas umständlich, was sich aber wahrscheinlich ändert, wenn man sich mehr damit befasst ;)

    Ich würde die Datei einmal öffnen und den Inhalt an einen Namen binden, anstatt alle 20 Sekunden den gleichen Inhalt aus einer Datei auszulesen.

    Die Datei wird halt von Zeit zu Zeit verändert (über Weboberfläche), weshalb ich die Datei regelmäßig aufrufen lasse. Wenn ich das so mache wie von dir vorgeschlagen, ist die Weckzeit ja quasi unveränderlich, da die Datei ja nur beim Start des Skriptes (sprich beim Neustart des Raspberrys) ausgelesen wird, oder? Bitte gerne korrigieren, wenn ich falsch liege ;)

    Danke übrigens für die veränderten Codevorschläge, werde ich bei Gelegenheit mal testen :)

    Im Prinzip will ich folgendes bewirken:

    Stand jetzt ist die einzige Funktion des Knopfes das Ausschalten des Weckers, was auch super funktoniert.

    Jetzt möchte ich den Code so ergänzen, dass bei Knopfdruck der Wecker ausgeschaltet wird und ein Zeitfenster entsteht, in dem bei erneutem Drücken des Knopfes ein Countdown eingeleitet wird, nach dessen Ende sich der Raspberry ausschaltet. Während des Countdowns soll das Herunterfahren aber durch einen erneuten Knopfdruck auch abgebrochen werden können.

    Symbolisiert werden die 3 Zustände (Herunterfahren möglich, Countdown, Abbruch erfolgreich) durch unterschiedliche LED-Zustände (leuchten bzw. blinken).

  • Ich habe es auch mit

    global modus

    Es geht schon, aber wie du siehst wird überall 'modus' verändert und und man kann schnell nicht mehr nachvollziehen wo 'modus' welchen Wert/Zustand hat und das wird schnell unübersichtlich und führt zu Fehlern. Also bitte kein 'global' verwenden.

    Die Datei wird halt von Zeit zu Zeit veränder

    Ja dann ist mein Vorschlag natürlich Quatsch. :daumendreh2:

    Zum Rest schreibe ich nachher was, ich wurde zum essen gebeten ^^

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Jetzt möchte ich den Code so ergänzen[...]

    Meiner Meinung nach musst du dir auch hier die Zustände merken. Ich konnte ohne OOP keine Lösung finden.

    Die grobe Struktur würde bei mir so aussehen:

    Ob dass mit dem Zeitfenster und dem Abbruch so funktioniert müsstest du mal testen. Ich habe gerade keinen Pi frei und um den Button zu simulieren ists mir jetzt zu spät.

    Viel Erfolg und Gute Nacht

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Ich konnte ohne OOP keine Lösung finden.

    Im Zweifel könnte man den Status (modus) auch in die Datei /home/pi/Python/uhrzeit.txt schreiben und auslesen. :conf: Toml wäre dazu aber imho besser geeignet, zumal es ein Modul für Python dafür gibt.

    Nach einem Stromausfall wären dann zumindest noch alle benötigten Variablen da, insoweit der RPi dann noch hoch kommen solte.

  • Hallo,

    obriger Code von mir wird nicht funktionieren. Ich denke man müsste zum Abbrechen des Countdowns den Callback des Buttons ändern. Ob dass dann aber alles so parallel abläuft oder ob man noch ein Thread starten muss :conf:

    Ich poste hier jetzt noch mein letztes Konzept zu dieser Problemstellung, ich denke aber, dass ich damit dem TE leider nicht wirklich weiter helfen kann.

    Das Problem ist meiner Meinung nach sicherlich etwas komplexer, als es sich auf den ersten Augenblick anhört.

    Ungetestet und wie gesagt, eventuell muss man noch 'threading' einabuen:

    Im Zweifel könnte man den Status (modus) auch in die Datei /home/pi/Python/uhrzeit.txt schreiben und auslesen.

    Ja, das wäre sicherlich ein workaround, wenn man sich nicht mit OOP beschäftigen will.

    Bin mal gespannt was ein richtiger Programmierer hier noch im allgemeinen zu dem Thema sagt.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • ich lese hier Wecker.

    Da fällt mir eine gute library dazu ein.

    schedule

    Die Klassen- bzw. Objektprogrammierung in Python ist finde ich etwas umständlich.....

    naja wie du vllt selbst fest gestellt hast ist es irgendwann nötig sich damit zu beschäftigen.

    Umständlich ist das sicher nicht. Code wird lesbarer/weniger.

    Hier ne Seite auf deutsch mit dem Recht guten Beispiel.

    Python-lernen OOP

  • Vielen Dank für eure vielen guten ausführlichen Antworten :danke_ATDE:

    Ich denke ich werde das Problem erstmal mit einem 2. Button auf dem Breadboard lösen und in den nächsten Wochen dann mit der OOP beginnen.

    Wobei, da fällt mir gerade ein, im Prinzip entsteht dann ja das gleiche Problem mit den Variablen jetzt.

    Vielleicht fällt jemandem hier ja noch etwas ein, ich wäre sehr dankbar dafür.

    Ich hätte auch nie gedacht, dass aus einer für mich unerklärlichen Fehlermeldung für eine Variable so eine Diskussion entsteht. Wie auch immer, TOP :thumbup:

  • ich lese hier Wecker.

    Da fällt mir eine gute library dazu ein.

    Bitte nicht die aus der Standard-Bibliothek. Schonmal versucht mit datetime + Zeitzonen zu arbeiten?

    Ok, ich habs mal jetzt versucht mit Zeitzonen.

    Code läuft nur mit Python 3.9

    Zu spät gesehen: schedule != sched

    Von zoneinfo gibt es einen backport.

    Jedenfalls ist mir das mit sched zu kompliziert.

    Einmal editiert, zuletzt von RestlessMud46765 (24. August 2021 um 11:55)

  • Juhu ein Programmierer ^^

    @DeaD_EyE weist du eine Möglichkeit, wie/ob man das Button-Problem ohne objektorientierte Programmierung lösen könnte?

    Siehe hier:

    Jetzt möchte ich den Code so ergänzen, dass bei Knopfdruck der Wecker ausgeschaltet wird und ein Zeitfenster entsteht, in dem bei erneutem Drücken des Knopfes ein Countdown eingeleitet wird, nach dessen Ende sich der Raspberry ausschaltet. Während des Countdowns soll das Herunterfahren aber durch einen erneuten Knopfdruck auch abgebrochen werden können.

    Symbolisiert werden die 3 Zustände (Herunterfahren möglich, Countdown, Abbruch erfolgreich) durch unterschiedliche LED-Zustände (leuchten bzw. blinken).

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Hallo, gibt es schon Neuigkeiten wie es weiter geht?

    Hallo :)

    Ich habe mich ins Thema objektorientierte Programmierung eingelesen und blicke mehr oder weniger durch.

    Die geposteten Codes verstehe ich allerdings nicht zur Gänze, z.B.:

    a) In Beitrag #3 wurden die Attribute der Klasse AlarmClock nicht in die Methode __init__ geschrieben (nur def __init__(self) statt def __init__(self, player, led, usw.).

    b) In Beitrag #7 wurde mit lambda gearbeitet - das ist mir momentan auch noch zu hoch.

    Generell verstehe ich den Ausbau der Codes sinngemäß, aber genau Zeile für Zeile kann ich sie nicht erklären.

    Auch wenn ich die OOP jetzt, wie gesagt, mehr oder weniger verstehe und fallweise anwenden könnte (Stichwort Katze, Auto, usw.), fällt es mir schwer, das Gelernte auf dieses Problem mit dem Wecker umzusetzen. Aber hoffentlich kommt von eurerseite noch der passende Input ;)


    @DeaD_EyE weist du eine Möglichkeit, wie/ob man das Button-Problem ohne objektorientierte Programmierung lösen könnte?

    auf das hoffe ich jetzt auch ^^

    Liebe Grüße

    Michael

  • Arcturus Ad a) Es wäre vielleicht besser den ein oder anderen Wert für ein Attribut dort, oder zumindest einen Wert mit dem das Attribut dann erstellt werden kann, als Argument zu übergeben, aber grundsätzlich muss das ja nicht sein. Man würde sich in einigen Fällen die API und das erstellen von Objekten aus einer Klasse unnötig kompliziert machen, wenn man grundsätzlich alle Attribute beim Aufruf übergeben müsste.

    Ad b) ``lambda``-Ausdrücke definieren eine anonyme Funktion. Anonym, weil die keinen Namen hat. Man könnte halt auch eine Funktion definieren, und muss sich dafür dann einen Namen ausdenken, und kann die Funktion dann zuweisen:

    Python
    def change_callback_function(alarm_clock):
        if alarm_clock.alarm_button_state:
            def named_function():
                return control_alarm_clock(alarm_clock)
            alarm_clock.alarm_button.when_pressed = named_function
        else:
            def named_function():
                return stopp_countdown(alarm_clock)
            alarm_clock.alarm_button.when_pressed = named_function

    Alternativ gibt es für diesen Fall, wo es ja um das binden von einem Argument und damit das reduzieren einer Funktion mit einem Argument auf eine mit keinem Argument geht, `functools.partial()`:

    Python
    from functools import partial
    
    ...
    
    
    def change_callback_function(alarm_clock):
        if alarm_clock.alarm_button_state:
            alarm_clock.alarm_button.when_pressed = partial(control_alarm_clock, alarm_clock)
        else:
            alarm_clock.alarm_button.when_pressed = partial(stopp_countdown, alarm_clock)

    Beziehungsweise kann man das dann auch mit ein bisschen weniger Code-Wiederholung ausdrücken:

    Python
    def change_callback_function(alarm_clock):
        alarm_clock.alarm_button.when_pressed = partial(
            (
                control_alarm_clock
                if alarm_clock.alarm_button_state
                else stopp_countdown
            ),
            alarm_clock,
        )

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!