Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

paho-mqtt und vorgegebene Callback-Funktionen in einer Klasse.

13 views
Skip to first unread message

Marc Haber

unread,
Jun 11, 2023, 1:12:14 PM6/11/23
to
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

Hartmut Goebel

unread,
Jun 11, 2023, 2:53:25 PM6/11/23
to
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-bringt-keine-sicherheit/

Marc Haber

unread,
Jun 11, 2023, 3:41:08 PM6/11/23
to
Hartmut Goebel <h.go...@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?

Peter J. Holzer

unread,
Jun 11, 2023, 4:06:40 PM6/11/23
to
On 2023-06-11 19:41, Marc Haber <mh+usene...@zugschl.us> wrote:
> Hartmut Goebel <h.go...@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

Hartmut Goebel

unread,
Jun 11, 2023, 4:17:10 PM6/11/23
to
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)

Stefan Clauß

unread,
Jun 21, 2023, 8:02:08 AM6/21/23
to
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+usene...@zugschl.us>:
Erstellt mit Operas E-Mail-Modul: http://www.opera.com/mail/

0 new messages