callback vs generator based design
Asked Answered
C

2

3

I'd like to get your advice for a design. I've got an Oven controlling the temperature and I'm doing some temperature dependent measurements. I'm basically setting the temperature, measure some stuff and move on.

I came up with two designs, simplified of course, which are shown below. The first one uses a callback based approach:

class Oven(object):
    # ... some methods
    def step_temperature(start, stop, num, rate, callback):
        temperatures = np.linspace(start, stop, num)
        for t in temperatures:
            self.temperature = t, rate # sweep to temperature with given rate
            self._wait_for_stability() # wait until temperature is reached.
            callback(t)                # execute the measurement
# Use Case
oven = Oven()
oven.step_temperature(start=20, stop=200, num=10, rate=1, callback=measure_stuff)

The second design is a generator based design

class Oven(object):
    # ... some methods

    def step_temperature(start, stop, num, rate):
        temperatures = np.linspace(start, stop, num)
        for t in temperatures:
            self.temperature = t, rate
            self._wait_for_stability()
            yield t
# Use Case
oven = Oven()
for t in oven.step_temperature(start=20, stop=200, num=10, rate=1):
    measure_stuff(t)

I'm tending towards the second design, but I'm interrested in your suggestions. If there is a even better way don't hesitate to tell me.

Clie answered 7/11, 2013 at 19:39 Comment(2)
The two implementations have much different properties. The first one is "blocking", while the second one allows to stop and resume the computation (which might or not be what you want). If you'll always use it in a for x in the_generator(): callback(x) then I see no reason to use a generator, just call the callback inside the method.Selftaught
Generators are nice to implement fancy algorithms. But to automate some measurement, which is usually some time-based procedure, I would stick to standard procedural code, so my vote would be for the first solution.Dichroic
F
2

@P3trus. I recently answered a very similar Python "yield versus callback" question on StackExchange's CodeReview. If you want to read it, here's a link but I'll summarize:

There are three common patterns used to solve the "report feedback" requirement:

  1. yield
  2. a callback function
  3. inline, hard-coded feedback

Both yield and callbacks allow you to separate the presentation details of UI/IO from the model or computation code. This is good. Both work well.

If you go with Python's yield, ensure you understand iterables and generators well, since several languages implement a yield keyword but have subtle implementation differences that might surprise you if, for example, you're used to C#'s yield. Here's a reference on that -- it's subtle but worth reading. Essentially, in Python when your function yields, it returns a generator which can be usefully assigned to a variable, capturing iteration to that point, but you may or may not want to do that. Don't let that scare you; yield is good.

Callbacks do have a nice advantage in that they allow communication back to the caller (in the model or computational code) which can be used to pause or halt processing or otherwise send a message to the model. This is a nice separation of duties and it allows communication that may be more difficult if you use yield. Or maybe not. There is always a way. :)

For example, while updating the reported temperature the callback can also monitor buttons or keys and return a value to the caller which could indicate, for example, that the user wants to abort processing and this doesn't pollute the model with awareness of specific UI or IO.

So instead of just:

callback(t)

You can let callback do its temperature reporting job but also listen to what it may have collected from the user, such as:

if callback(t) == ABORT_BUTTON_PRESSED:
    self.shutdown  # or whatever

Hope this helps.

Fertilize answered 7/11, 2013 at 19:56 Comment(0)
R
0

If you occasionally do different stuff with t per call of step_temperature, (like, you call it, and some of the results, you do one thing with, while some you do another with), you'd want the generator version. If every time you call step_temperature, you want the same thing done to every item, and no differing computation between, I'd use the callback version. That way anyone who uses your code doesn't have to know when to call their processing function for t.

Rhadamanthus answered 7/11, 2013 at 19:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.