Proper way to deal with external objects with functions in Python
Question:
This implementation of the AWS MQTT broker is confusing me.
In the code, there is this function definition:
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
global received_count
received_count += 1
if received_count == args.count:
received_all_event.set()
And then the function is called like this:
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
There are two things which are confusing me:
- Why the on_message_received function is called without parameters?
- If I want to pass a variable to on_message_received and do something to it from within that function, which would be the correct approach?
About point (2), consider the following example, which seems to be working:
last_message = ''
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
last_message = payload
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
But I don’t think it’s the correct approach. But I don’t know how I should pass the external variable to the function.
Answers:
The example you posted doesn’t actually work because last_message
is a local variable — you can’t access it from outside the function. I’d suggest putting it in a handler object, like this:
class MessageHandler:
def __init__(self):
self.last_message = ''
def on_message_received(self, topic, payload, **_kwargs):
print(f"Received message from topic '{topic}': {payload}")
self.last_message = payload
handler = MessageHandler()
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=handler.on_message_received
)
subscribe_result = subscribe_future.result()
print("Last message:", handler.last_message)
The on_message_received
function is not called by your code, it is passed as an argument to mqtt_connection.subscribe
. The API is then what will call it (later, when there’s a message), and at that time it should provide the topic
and payload
args to your function.
Calling subscribe_future.result()
causes your program to stop and wait for a message to be received (i.e. it waits for the Future
object to produce a result
). Note that in real life you usually don’t want to immediately block on a future (because the point of a future is that it’s something that will happen in the future and you probably have other things to do while you wait for that future to happen).
This implementation of the AWS MQTT broker is confusing me.
In the code, there is this function definition:
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
global received_count
received_count += 1
if received_count == args.count:
received_all_event.set()
And then the function is called like this:
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
There are two things which are confusing me:
- Why the on_message_received function is called without parameters?
- If I want to pass a variable to on_message_received and do something to it from within that function, which would be the correct approach?
About point (2), consider the following example, which seems to be working:
last_message = ''
def on_message_received(topic, payload, **kwargs):
print("Received message from topic '{}': {}".format(topic, payload))
last_message = payload
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received
)
subscribe_result = subscribe_future.result()
But I don’t think it’s the correct approach. But I don’t know how I should pass the external variable to the function.
The example you posted doesn’t actually work because last_message
is a local variable — you can’t access it from outside the function. I’d suggest putting it in a handler object, like this:
class MessageHandler:
def __init__(self):
self.last_message = ''
def on_message_received(self, topic, payload, **_kwargs):
print(f"Received message from topic '{topic}': {payload}")
self.last_message = payload
handler = MessageHandler()
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=args.topic,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=handler.on_message_received
)
subscribe_result = subscribe_future.result()
print("Last message:", handler.last_message)
The on_message_received
function is not called by your code, it is passed as an argument to mqtt_connection.subscribe
. The API is then what will call it (later, when there’s a message), and at that time it should provide the topic
and payload
args to your function.
Calling subscribe_future.result()
causes your program to stop and wait for a message to be received (i.e. it waits for the Future
object to produce a result
). Note that in real life you usually don’t want to immediately block on a future (because the point of a future is that it’s something that will happen in the future and you probably have other things to do while you wait for that future to happen).