paho-mqtt und vorgegebene Callback-Funktionen in einer Klasse.
Hallo, ich habe hier das abgespeckte, nicht lauffähige Teilprogramm: |#!/usr/bin/python | |import paho.mqtt.client as mqtt | |class ApplianceMonitor: | """the main ApplianceMonitor object""" | | def __init__(self): | # do something intelligent | | def on_connect(self, client, userdata, flags, rcode): | client.subscribe('tele/wama/SENSOR') | | def on_message(self, client, userdata, msg): | # do something intelligent with msg | | def main(self): | client = mqtt.Client() | client.on_connect = on_connect | client.on_message = on_message | | client.connect('mqtt.local', 1883, 60) | | client.loop_start() | | while True: | time.sleep(30) | # do something intelligent with the accumulated data | | |if __name__ == "__main__": | amon = ApplianceMonitor(args) | amon.main() | |# end of file Ich habe die ganze Applikation in eine Klasse ApplianceMonitor ausgelagert als Ergebnis der Diskussion von vor einem halben Jahr, weil ich so ohne globale Statusvariablen auskomme und die in der Klasse verstecken kann (hier nicht sichtbar weil sie der Minimierung des Codes zum Opfer gefallen sind). Leider haben die beiden Callbackfunktionen von paho eine vorgegebene Signatur und können deswegen keine Methoden des ApplianceMonitor sein. Wenn ich die Initialisierung des MQTT-Moduls ins Hauptprogramm verlagere, bin ich auf ein einziges Applikationsobjekt limitiert (was in der Tat blöd ist weil ich vielleicht später das Monitoring mehrerer MQTT-Topics mit mehreren Objekten des Applikationstyps machen möchte. Oder geht das sowieso nicht weil ich nur eine client.loop pro Programm haben kann? Ist das paho-Modul blöd oder ich? Oder geht das halt nicht anders? Grüße Marc -- -------------------------------------- !! No courtesy copies, please !! ----- Marc Haber | " Questions are the | Mailadresse im Header Mannheim, Germany | Beginning of Wisdom " | Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 621 72739834
Hallo, ich kenne die Diskussion von vor einem halbe Jahr nicht, aber mir fällt folgendes auf: Am 11.06.23 um 19:12 schrieb Marc Haber:
| def on_connect(self, client, userdata, flags, rcode): | client.subscribe('tele/wama/SENSOR') [_] | client = mqtt.Client() | client.on_connect = on_connect
Diese Zeile muss lauten: client.on_connect =*self.*on_connect Es wundert mich, dass Dein Code überhaupt funktioniert, denn "on_connect" ist nicht definiert. Oder hast Du das nur zu viel gekürzt?!
Leider haben die beiden Callbackfunktionen von paho eine vorgegebene Signatur und können deswegen keine Methoden des ApplianceMonitor sein.
Hmm, Ich habe eben folgendes probiert, und das klappt (X.on_connect wird wie erwartet mit der Instanz von ApplianceMonitor als ersten Argument und den anderen vier danach aufgerufen): class Client: def doit(self): self.on_connect(self, 2, 3, 4) class ApplianceMonitor: def on_connect(self, client, userdata, flags, rcode): print(self, client, userdata, flags, rcode) def main(self): c = Client() c.on_connect = self.on_connect c.doit() ApplianceMonitor().main() Andernfalls verstehe ich nicht, was Deine Frage ist. -- Schönen Gruß Hartmut Goebel Dipl.-Informatiker (univ), CISSP, CSSLP, ISO 27001 Lead Implementer Information Security Management, Security Governance, Secure Software Development Goebel Consult, Landshut http://www.goebel-consult.de Blog: https://www.goebel-consult.de/blog/2021/debugging-python-_frozen_importlib/ Kolumne: https://www.goebel-consult.de/blog/cissp-gefluester/2012-04-compliance-bring...
Hartmut Goebel <h.goebel@goebel-consult.de> wrote:
Am 11.06.23 um 19:12 schrieb Marc Haber:
| def on_connect(self, client, userdata, flags, rcode): | client.subscribe('tele/wama/SENSOR') [_] | client = mqtt.Client() | client.on_connect = on_connect
Diese Zeile muss lauten:
client.on_connect =*self.*on_connect
Es wundert mich, dass Dein Code überhaupt funktioniert, denn "on_connect" ist nicht definiert. Oder hast Du das nur zu viel gekürzt?!
Der Code hat nicht funktioniert, das hatte ich gar nicht probiert weil ich ja wusste dass da irgendwas nicht passt. Kannst Du mir erklären, warum das funktioniert obwohl die Memberfunktion den zusätzlichen self-Parametrer hat? Grüße Marc -- -------------------------------------- !! No courtesy copies, please !! ----- Marc Haber | " Questions are the | Mailadresse im Header Mannheim, Germany | Beginning of Wisdom " | Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 621 72739834
On 2023-06-11 19:41, Marc Haber <mh+usenetspam1118@zugschl.us> wrote:
Hartmut Goebel <h.goebel@goebel-consult.de> wrote:
Am 11.06.23 um 19:12 schrieb Marc Haber:
| def on_connect(self, client, userdata, flags, rcode): | client.subscribe('tele/wama/SENSOR') [_] | client = mqtt.Client() | client.on_connect = on_connect
Diese Zeile muss lauten:
client.on_connect =*self.*on_connect
Es wundert mich, dass Dein Code überhaupt funktioniert, denn "on_connect" ist nicht definiert. Oder hast Du das nur zu viel gekürzt?!
Der Code hat nicht funktioniert, das hatte ich gar nicht probiert weil ich ja wusste dass da irgendwas nicht passt.
Kannst Du mir erklären, warum das funktioniert
Ich bin mir nicht ganz sicher, was Du mit "das" meinst, aber ich vermute:
obwohl die Memberfunktion den zusätzlichen self-Parametrer hat?
Die Expression self.on_connect ergibt eine "bound method". Die ist also jetzt an das Objekt gebunden, und wenn man aufruft, wird das gebundene Objekt an seine Methode übergeben. Vergleiche: #!/usr/bin/python3 class C: def m(self, *args, **kwargs): print(self, args, kwargs) o = C() print(o) f = o.m print(f) f(1, 2, 3) f(1, x=2, y=3) Das ist auf den ersten Blick überraschend, aber »o.m()« ist ja syntaktisch nichts anderes als »(o.m)()«, also sollte man das in »f = o.m; f()« zerlegen können. Und so ist es auch. hp
Am 11.06.23 um 21:41 schrieb Marc Haber:
Kannst Du mir erklären, warum das funktioniert obwohl die Memberfunktion den zusätzlichen self-Parametrer hat?
Nicht so richtig — also nicht von der Sprachdefinition her :-) Aber die Methode ist an das Objekt/Instanz gebunden:
amon.on_connect <bound method ApplianceMonitor.on_connect of <__main__.ApplianceMonitor object at 0x7f560d9ecee0>>
Das impliziert, dass die Instanz als erster Parameter übergeben wird. https://docs.python.org/3/tutorial/classes.html#method-objects erklärt dann doch noch was: "amon.on_connect" liefert eben nicht die Funktion "ApplianceMonitor.on_connetct", sondern die Methode "on_connect" für ("bound to") die Instanz "amon". Da steckt quasi "self" als erster Parameter mit drin. Ähnliches könntest Du erreichen mit functools.partial(ApplianceMonitor.on_connect, anom) -- Schönen Gruß Hartmut Goebel Dipl.-Informatiker (univ), CISSP, CSSLP, ISO 27001 Lead Implementer Information Security Management, Security Governance, Secure Software Development Goebel Consult, Landshut http://www.goebel-consult.de Blog: https://www.goebel-consult.de/blog/2021/debugging-python-_frozen_importlib/ Kolumne: https://www.goebel-consult.de/blog/cissp-gefluester/2012-04-compliance-bring...
Hallo, versuche es mit self.on_connect und self.on_message jeweils auf der rechten Seite der Zuweisung. VG Stefan Am .06.2023, 19:12 Uhr, schrieb Marc Haber <mh+usenetspam1118@zugschl.us>:
Hallo,
ich habe hier das abgespeckte, nicht lauffähige Teilprogramm:
|#!/usr/bin/python | |import paho.mqtt.client as mqtt | |class ApplianceMonitor: | """the main ApplianceMonitor object""" | | def __init__(self): | # do something intelligent | | def on_connect(self, client, userdata, flags, rcode): | client.subscribe('tele/wama/SENSOR') | | def on_message(self, client, userdata, msg): | # do something intelligent with msg | | def main(self): | client = mqtt.Client() | client.on_connect = on_connect | client.on_message = on_message | | client.connect('mqtt.local', 1883, 60) | | client.loop_start() | | while True: | time.sleep(30) | # do something intelligent with the accumulated data | | |if __name__ == "__main__": | amon = ApplianceMonitor(args) | amon.main() | |# end of file
Ich habe die ganze Applikation in eine Klasse ApplianceMonitor ausgelagert als Ergebnis der Diskussion von vor einem halben Jahr, weil ich so ohne globale Statusvariablen auskomme und die in der Klasse verstecken kann (hier nicht sichtbar weil sie der Minimierung des Codes zum Opfer gefallen sind).
Leider haben die beiden Callbackfunktionen von paho eine vorgegebene Signatur und können deswegen keine Methoden des ApplianceMonitor sein. Wenn ich die Initialisierung des MQTT-Moduls ins Hauptprogramm verlagere, bin ich auf ein einziges Applikationsobjekt limitiert (was in der Tat blöd ist weil ich vielleicht später das Monitoring mehrerer MQTT-Topics mit mehreren Objekten des Applikationstyps machen möchte. Oder geht das sowieso nicht weil ich nur eine client.loop pro Programm haben kann?
Ist das paho-Modul blöd oder ich? Oder geht das halt nicht anders?
Grüße Marc
-- Erstellt mit Operas E-Mail-Modul: http://www.opera.com/mail/
participants (4)
-
Hartmut Goebel
-
Marc Haber
-
Peter J. Holzer
-
Stefan Clauß