Seite 1 von 1
Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 10:34
von Hellstern
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
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 11:58
von grubenfox
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
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 12:07
von __blackjack__
@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
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 12:15
von DeaD_EyE
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
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 12:27
von grubenfox
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)
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 13:08
von grubenfox
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.
Re: Globale Variable umgehen
Verfasst: Dienstag 24. Januar 2023, 13:25
von DeaD_EyE
grubenfox hat geschrieben: Dienstag 24. Januar 2023, 13:08
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.