Learning Python - Mark Lutz [262]
Because arguments are passed by assigning objects, it’s just as easy to pass functions to other functions as arguments. The callee may then call the passed-in function just by adding arguments in parentheses:
>>> def indirect(func, arg):
... func(arg) # Call the passed-in object by adding ()
...
>>> indirect(echo, 'Argument call!') # Pass the function to another function
Argument call!
You can even stuff function objects into data structures, as though they were integers or strings. The following, for example, embeds the function twice in a list of tuples, as a sort of actions table. Because Python compound types like these can contain any sort of object, there’s no special case here, either:
>>> schedule = [ (echo, 'Spam!'), (echo, 'Ham!') ]
>>> for (func, arg) in schedule:
... func(arg) # Call functions embedded in containers
...
Spam!
Ham!
This code simply steps through the schedule list, calling the echo function with one argument each time through (notice the tuple-unpacking assignment in the for loop header, introduced in Chapter 13). As we saw in Chapter 17’s examples, functions can also be created and returned for use elsewhere:
>>> def make(label): # Make a function but don't call it
... def echo(message):
... print(label + ':' + message)
... return echo
...
>>> F = make('Spam') # Label in enclosing scope is retained
>>> F('Ham!') # Call the function that make returned
Spam:Ham!
>>> F('Eggs!')
Spam:Eggs!
Python’s universal object model and lack of type declarations make for an incredibly flexible programming language.
Function Introspection
Because they are objects, we can also process functions with normal object tools. In fact, functions are more flexible than you might expect. For instance, once we make a function, we can call it as usual:
>>> def func(a):
... b = 'spam'
... return b * a
...
>>> func(8)
'spamspamspamspamspamspamspamspam'
But the call expression is just one operation defined to work on function objects. We can also inspect their attributes generically (the following is run in Python 3.0, but 2.6 results are similar):
>>> func.__name__
'func'
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
...more omitted...
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Introspection tools allow us to explore implementation details too—functions have attached code objects, for example, which provide details on aspects such as the functions’ local variables and arguments:
>>> func.__code__
>>> dir(func.__code__) ['__class__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', ...more omitted... 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] >>> func.__code__.co_varnames ('a', 'b') >>> func.__code__.co_argcount 1 Tool writers can make use of such information to manage functions (in fact, we will too in Chapter 38, to implement validation of function arguments in decorators). Function Attributes Function objects are not limited to the system-defined attributes listed in the prior section, though. As we learned in Chapter 17, it’s possible to attach arbitrary user-defined attributes to them as well: >>> func >>> func.count = 0 >>> func.count += 1 >>> func.count 1 >>> func.handles = 'Button-Press' >>> func.handles 'Button-Press' >>> dir(func) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', ...more omitted... __str__', '__subclasshook__', 'count', 'handles'] As we saw in that chapter, such attributes can be used to attach state information to function objects directly, instead of using other techniques such as globals, nonlocals, and classes. Unlike nonlocals, such attributes are accessible anywhere the function itself is. In a sense, this is also a way to emulate", line 1>