If you had used a GUI framework like Qt, then you must have met signals. Signals provide an easy way to handle events in your program. For instance, when a button widget is created, you don’t need to code right there what should be executed when the button is clicked. Instead, the button emits the clicked signal and whatever callback is subscribed to that signal is executed to perform the desired action.
You can also add this functionality to simple scripts that have no GUI interfaces at all. Let’s see two solutions:
“Blinker provides fast & simple object-to-object and broadcast signaling for Python objects.”
The Flask project too uses Blinker for signal processing.
from blinker import signal
>>> started = signal('round-started')
>>> def each(round):
... print "Round %s!" % round
>>> def round_two(round):
... print "This is round two."
>>> started.connect(round_two, sender=2)
>>> for round in range(1, 4):
# Round 1!
# Round 2!
# This is round two.
# Round 3!
How to read it? First, we create a global signal called “
started“. The line “
started.connect(each)” means: if the “
started” signal is emitted (i.e. if this event happens), then call the “
Notice the “
round” parameter of the “
each” function: when a signal is emitted, it can transmit an object, i.e. at the place of signal emission you can send an arbitrary object with the signal.
The line “
started.connect(round_two, sender=2)” means: if the “
started” signal is emitted, then call the “
round_two” function ONLY IF the object “
2” is sent with the signal.
Then there is a loop from 1 to 3. In the loop we emit the “
started” signal and the numbers are sent together with the signal. When the signal is emitted with “
each” is called. When the signal is emitted with “
2“, first “
each” is called, then “
round_two” is also executed since the signal holds the object “
2” (the functions are called in the order of registration). Finally “
each” is executed again with the number “
Example #2 (taken from here):
self.two = Two()
def callback(self, data): # notice the data parameter
some_signal = signal('some_signal')
# Do something
one = One()
“In code above, the Two object doesn’t even know if there’s some other object interested in its internal state changes. However, it does notify about them by emitting a signal that might be used by other objects (in this case a One object) to perform some specific action.” (by jcollado @SO)
The project smokesignal is lighter than blinker.
from time import sleep
print "#", val
# smokesignal.on('debug', verbose)
# smokesignal.on('debug', verbose, max_calls=5) ## respond max. 5 times to the signal
# smokesignal.once('debug', verbose) ## max_calls=1 this time
for i in range(100):
if i and i%10==0:
It’s very similar to blinker. First we do the registration: if the “
debug” signal is emitted, then execute the function “
verbose“. In the loop if “
i” is 10, 20, etc., then emit the signal “
debug” and attach the value of “
i” to the signal.
Smokesignal is just one file, thus it’s very easy to add to a project. However, it has a disadvantage:
“What would be great is if you could decorate instance methods… However, that doesn’t work because there is no knowledge of the class instance at the time the callback is registered to respond to signals.” (source)
They have a workaround but I find it ugly.
As seen in Example #2, with blinker you can register instance methods to be called when a signal is emitted.
If you want a lightweight solution without instance methods, use smokesignal. If you also want to call instance methods when an event happens, use blinker.