Globale Variable umgehen

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
Hellstern
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2023, 08:14

Hallo zusammen
Ich benötige eine Funktion welche einen Takt vorgibt, welcher in anderen Funktionen verwendet wird.
Meine Lösung funktioniert soweit, aber nur mit einer globalen Variablen
Hat jemand eine Idee, wie man das ändern könnte?

def runNext(takt, intervall):
global sekCount
if takt == True:
sekCount = sekCount + 1
if sekCount >= intervall:
sekCount = 0
return True
else:
return False


Gruss
Thomas
Benutzeravatar
grubenfox
User
Beiträge: 431
Registriert: Freitag 2. Dezember 2022, 15:49

Mein erster Gedanke: Den Zähler und die Funktion in eine Klasse kippen und um zu verhindern dass von der klasse mehrfach unterschiedliche Objekte erzeugt werden, das Singleton-Entwurfsmuster benutzen.

Um das Singleton-Entwurfsmuster in Python umzusetzen gibt es verschiedene Möglicheiten: https://www.geeksforgeeks.org/singleton ... ete-guide/

Wobei auf der Webseite die erste mögliche Umsetzung ist das ganze auf Modul-Ebene zu verwenden. Also doch keine Klasse, mach einfach ein Modul draus...

Datei taktgeber.py:

Code: Alles auswählen

sekCount = 0

def run_next(takt, intervall):
    if takt == True:
        sekCount = sekCount + 1
    if sekCount >= intervall:
        sekCount = 0
        return True
    else:
        return False
Fertig...

In der Nutzung:

Datei mach_was_wichtiges_im_takt.py:

Code: Alles auswählen

from taktgeber import run_next

if run_next(True, 42):
    # mache hier irgendwas wichtiges
Ungetestet, aber sollte eigentlich laufen denke ich
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hellstern: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten auch keine kryptischen Abkürzungen enthalten. SEK ist ja etwas geläufiger, da muss man nicht so lange raten, da ist dann aber nicht gut Deutsch und Englisch in einem Namen zu mischen. Also besser auf eine Sprache festlegen.

Man macht keine Vergleiche mit literalen Wahrheitswerten. Bei dem Vergleich kommt doch nur wieder ein Wahrheitswert bei heraus. Entweder der, den man sowieso schon hatte; dann kann man den auch gleich nehmen. Oder das Gegenteil davon; dafür gibt es ``not``.

Wenn man sich Zustand über Aufrufe hinweg merken will, verwendet man Klassen.

Code: Alles auswählen

class SWATCounter:
    def __init__(self):
        self.swat_count = 0

    def run_next(self, takt, intervall):
        self.swat_count += int(takt)
        result = self.swat_count >= intervall
        if result:
            self.swat_count = 0
        return result
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Einfach global zu entfernen, bringt nichts.

Code: Alles auswählen

sekCount = 0

def run_next(takt, intervall):
    if takt == True:
        sekCount = sekCount + 1 # <- UnboundLocalError
    if sekCount >= intervall:
        sekCount = 0
        return True
    else:
        return False

Mit einer Klasse.

Könnte so aussehen:

Code: Alles auswählen

import RPi.GPIO as gpio


class SignalCounter:
    def __init__(self, intervall, takt_pin):
        self.seek_count = 0
        self.intervall = intervall
        self.takt_pin = takt_pin
        gpio.setup(takt_pin, gpio.IN, pull_up_down=GPIO.PUD_DOWN)
        
    def run_next(self):
        """
        polling
        https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/
        """
        
        if gpio.input(self.takt_pin):
            self.seek_count += 1
            
        if self.seek_count >= self.intervall:
            self.seek_count = 0
        
        # not bool(0) -> True
        # not bool(1) -> False
        return not bool(self.seek_count)


if __name__ == __main__:
    gpio.setmode(gpio.BCM)
    
    sc = SignalCounter(10, 22)
    # wodurch wird run_next aufgerufen?!
    
    while True:
        sc.run_next()
        time.sleep(0.1)
        # intervall von 100 ms, alles was dazwischen
        # passiert, geht einfach verloren
        
Code ist nicht getestet. Die Namen habe ich mal an die Konventionen angepasst.

Man könnte das auch mit Funktionen erreichen, muss dann aber alle benötigen Argumente immer strikt mit angeben. Man hat auch nicht immer die Kontrolle darüber, was einer Funktion an Argumenten übergeben wird, wenn man z.B. Callbacks nutzt. Die aufrufende Funktion (z.B. von GPIO) bestimmt die Argumente. Bei RPi.GPIO wird bei einem Interrupt die Pin-Nummer der Funktion übergeben. Wenn man dann in diesem Aufruf weitere Argumente benötigt, wird es kompliziert. Da müsste dann man mit Closures (Funktion in Funktion) arbeiten.

Im Allgemeinen sind Klassen einfacher, wenn man sie verstanden hat.
Mit self ist die Instanz gemeint und auf self kann zugegriffen werden. Die Angab
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
grubenfox
User
Beiträge: 431
Registriert: Freitag 2. Dezember 2022, 15:49

DeaD_EyE hat geschrieben: Dienstag 24. Januar 2023, 12:15 Einfach global zu entfernen, bringt nichts.

Code: Alles auswählen

sekCount = 0

def run_next(takt, intervall):
    if takt == True:
        sekCount = sekCount + 1 # <- UnboundLocalError
    if sekCount >= intervall:
        sekCount = 0
        return True
    else:
        return False
Ah, deswegen haben sie auf der geekforgeeks-Seite im singleton.py nur die Variable shared_variable definiert und keine Funktion. Dann also doch eine Klasse, aber eben verhindern das mehrere unterschiedliche Taktgeber instanziert werden können (siehe oben bei geeforgeeks.org)
Benutzeravatar
grubenfox
User
Beiträge: 431
Registriert: Freitag 2. Dezember 2022, 15:49

Diesmal was getestetes

takt.py:

Code: Alles auswählen

## takt.py
def run_next(takt, intervall):
    if takt == True:
        run_next.zaehler = run_next.zaehler + 1
    if run_next.zaehler >= intervall:
        run_next.zaehler = 0
        return True
    else:
        return False

run_next.zaehler = 0
Auch wieder mit einer Klasse. Aber in Python ist ja alles eine Klasse, auch die Funktion run_next. Also nutzen wir sie.... Aber ich bin nicht sicher ob ich das so nutzen würde, auch wenn es funktioniert.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

grubenfox hat geschrieben: Dienstag 24. Januar 2023, 13:08

Code: Alles auswählen

def run_next(takt, intervall): ...
Auch wieder mit einer Klasse.

Die Funktion ist keine Klasse und es erscheint auch nicht auf automagische Weise das Attribut zaehler.
Man könnte unter anderem die Klasse types.SimpleNamespace als Container verwenden und dem Objekt ein Attribut zuweisen.
Geht auch mit ganz normalen Klassen.

Code: Alles auswählen

from types import SimpleNamespace

ns = SimpleNamespace(count=0)


def count_up():
    ns.count += 1

for _ in range(10):
    count_up()


print(ns.count)
Ich würde es dennoch mit Klassen machen, da man durch die Klassen eine bessere Isolierung vom Rest des Codes hat.
Wenn du noch nicht genau weißt, was Klassen sind, dann müsstest du dich damit erst mal intensivst mit auseinandersetzen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten