The callback function with additional state

Encoding a situation often encountered is the need to write callback functions, such as event handlers and so on. As a conventional general callback function, pass parameters, return value calculated.

def apply_async(func, args, *, callback): 
    # Compute the result
    result = func(*args)

    # Invoke the callback with the result
    callback(result)

def print_result(result):
    print('Got:', result)

def add(x, y):
    return x + y

>>> apply_async(add, (2, 3), callback=print_result)
Got: 5
>>> apply_async(add, ('hello', 'world'), callback=print_result)
Got: helloworld
>>>

Notes that, print_result () function takes only one parameter, which result. No other information transfer. When you want the callback function and other variables interact with the environment or in part, the lack of information sometimes cause problems.
Carry extra state in the callback or a way information is to use binding method instead of a simple function. For example, use the class definition. As follows:

class ResultHandler:
    def __init__(self):
        self.sequence = 0
    def handler(self, result):
        self.sequence += 1
        print('[{}] Got: {}'.format(self.sequence, result))

>>> r = ResultHandler()
>>> apply_async(add, (2, 3), callback=r.handler)
[1] Got: 5
>>> apply_async(add, ('hello', 'world'), callback=r.handler) 
[2] Got: helloworld
>>>

Examples of the use of the class attribute may carry its state sequence, and acquires its state each time and update a callback.
Without the use of class instance, closures can be used to capture the state of the way. as follows:

def make_handler():
    sequence = 0
    def handler(result):
        nonlocal sequence
        sequence += 1
        print('[{}] Got: {}'.format(sequence, result))
    return handler

>>> handler = make_handler()
>>> apply_async(add, (2, 3), callback=handler)
[1] Got: 5
>>> apply_async(add, ('hello', 'world'), callback=handler) 
[2] Got: helloworld
>>>

Another variation is to use a coroutine, as follows:

def make_handler(): 
    sequence = 0
    while True:
        result = yield
        sequence += 1
        print('[{}] Got: {}'.format(sequence, result))

>>> handler = make_handler()
>>> next(handler) # Advance to the yield
>>> apply_async(add, (2, 3), callback=handler.send)
[1] Got: 5
>>> apply_async(add, ('hello', 'world'), callback=handler.send) 
[2] Got: helloworld
>>>

Finally, and very importantly, you can also use the additional parameters and partial function to carry the state to the callback:

class SequenceNo:
    def __init__(self):
        self.sequence = 0

def handler(result, seq):
    seq.sequence += 1
    print('[{}] Got: {}'.format(seq.sequence, result))

seq = SequenceNo()
from functools import partial
apply_async(add, (2, 3), callback=partial(handler, seq=seq))
[1] Got: 5
apply_async(add, ('hello', 'world'), callback=partial(handler, seq=seq))
[2] Got: helloworld

Software-based callback function often braving the chaos of risk. Part of the problem is usually associated with the callback function caused by the initial request to disconnect callback code. Thus, the effective loss of the execution environment between the request and the processing result. If you want to continue with the callback process involves multiple steps, you must figure out how to save and restore the state association.

There are actually two main methods used to capture and carry status. It may be carried on a class instance (attached to the binding method), it can be carried in a closure (internal function). In both techniques, the closure may be lighter, more natural, because they are constructed only by the function. They also automatically capture all the variables being used. Thus, longer necessary to store the exact state (determined automatically by the code).

Coroutine use as a callback handler is interesting because it is closely related to the closure method. In a sense, it is even more clean, because it has only one function. In addition, variables can be modified freely, without fear of nonlocal statement.

Potential drawback is the rest of the Python coroutine not as easily understood. There are some difficult issues, such as the need to call next () before using coroutines on coroutines. In practice, it is easy to forget. Nevertheless, coroutine where other potential uses, e.g. inline callback (inlined callback) definition.

Guess you like

Origin www.cnblogs.com/jeffrey-yang/p/11986512.html