Function Interfaces and Callback-Based Code

As an example, the tkinter GUI toolkit (named Tkinter in Python 2.6) allows you to register functions as event handlers (a.k.a. callbacks); when events occur, tkinter calls the registered objects. If you want an event handler to retain state between events, you can register either a class’s bound method or an instance that conforms to the expected interface with __call__. In this section’s code, both x.comp from the second example and x from the first can pass as function-like objects this way.

I’ll have more to say about bound methods in the next chapter, but for now, here’s a hypothetical example of __call__ applied to the GUI domain. The following class defines an object that supports a function-call interface, but also has state information that remembers the color a button should change to when it is later pressed:

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

class Callback:
    def __init__(self, color):               # Function + state information
        self.color = color
    def __call__(self):                      # Support calls with no arguments
        print('turn', self.color)

Now, in the context of a GUI, we can register instances of this class as event handlers for buttons, even though the GUI expects to be able to invoke event handlers as simple functions with no arguments:

cb1 = Callback('blue')                       # Remember blue
cb2 = Callback('green')

B1 = Button(command=cb1)                     # Register handlers
B2 = Button(command=cb2)                     # Register handlers

When the button is later pressed, the instance object is called as a simple function, exactly like in the following calls. Because it retains state as instance attributes, though, it remembers what to do:

cb1()                                        # On events: prints 'blue'
cb2()                                        # Prints 'green'

In fact, this is probably the best way to retain state information in the Python language—better than the techniques discussed earlier for functions (global variables, enclosing function scope references, and default mutable arguments). With OOP, the state remembered is made explicit with attribute assignments.

Before we move on, there are two other ways that Python programmers sometimes tie information to a callback function like this. One option is to use default arguments in lambda functions:

cb3 = (lambda color='red': 'turn ' + color)  # Or: defaults
print(cb3())

The other is to use bound methods of a class. A bound method object is a kind of object that remembers the self instance and the referenced function. A bound method may therefore be called as a simple function without an instance later:

class Callback:
    def __init__(self, color):               # Class with state information
        self.color = color
    def changeColor(self):                   # A normal named method
        print('turn', self.color)

cb1 = Callback('blue')
cb2 = Callback('yellow')

B1 = Button(command=cb1.changeColor)         # Reference, but don't call
B2 = Button(command=cb2.changeColor)         # Remembers function+self

In this case, when this button is later pressed it’s as if the GUI does this, which invokes the changeColor method to process the object’s state information:

object = Callback('blue')
cb = object.changeColor                      # Registered event handler
cb()                                         # On event prints 'blue'

Note that a lambda is not required here, because a bound method reference by itself already defers a call till later (again, more on bound methods in Chapter 30). This technique is simpler, but less general than overloading calls with __call__; again, watch for more about bound methods in the next chapter.

You’ll also see another __call__ example in Chapter 31, where we will use it to implement something known as a function decorator—a callable object often used to add a layer of logic on top of an embedded function. Because __call__ allows us to attach state information to a callable object, it’s a natural implementation technique for a function that must remember and call another function.