So far, we’ve seen what it takes to write our own functions in Python. There are a handful of additional function-related ideas we’d like to introduce in this section:
lambda
creates anonymous functions.apply
calls functions with argument tuples.map
runs a function over a sequence and collects results.Functions return
None
if they don’t use areturn
statement.Functions present design choices.
Functions are objects, just like numbers and strings.
Besides the def
statement, Python also provides an
expression form that generates function objects. Because of its
similarity to a tool in the LISP language, it’s called
lambda
. Its general form is the keyword
lambda
, followed by one or more arguments,
followed by an expression after a colon:
lambda argument1, argument2,... argumentN : Expression using arguments
Function objects returned by lambda
expressions
are exactly the same as those created and assigned by
def
. But the lambda
has a few
differences that make it useful in specialized roles:
- lambda is an expression, not a statement
Because of this, a
lambda
can appear in places adef
can’t—inside a list constant, for example. As an expression, thelambda
returns a value (a new function), which can be assigned a name optionally; thedef
statement always assigns the new function to the name in the header, instead of returning it as a result.- lambda bodies are a single expression, not a block of statements
The
lambda
’s body is similar to what you’d put in adef
body’sreturn
statement; simply type the result as a naked expression, instead of explicitly returning it. Because it’s limited to an expression,lambda
is less general than adef
; you can only squeeze so much logic into alambda
body without using statements such asif
.
Apart from those distinctions, the def
and
lambda
do the same sort of work. For instance,
we’ve seen how to make functions with def
statements:
>>>def func(x, y, z): return x + y + z
... >>>func(2, 3, 4)
9
But you can achieve the same effect with a lambda
expression, by explicitly assigning its result to a name:
>>>f = lambda x, y, z: x + y + z
>>>f(2, 3, 4)
9
Here, f
is assigned the function object the
lambda
expression creates (this is how
def
works, but the assignment is automatic).
Defaults work on lambda
arguments too, just like
the def
:
>>>x = (lambda a="fee", b="fie", c="foe": a + b + c)
>>>x("wee")
'weefiefoe'
lambda
s come in handy as a shorthand for
functions. For instance, we’ll see later that callback handlers
are frequently coded as lambda
expressions
embedded directly in a registration call, instead of being defined
elsewhere in a file and referenced by name.
Some programs need to call arbitrary functions in a generic fashion,
without knowing their names or arguments ahead of time. We’ll
see examples of where this can be useful later, but by way of
introduction, the apply
built-in function does the job. For
instance, after running the code in the prior section, you can call
the generated functions by passing them as arguments to
apply
, along with a tuple of arguments:
>>>apply(func, (2, 3, 4))
9 >>>apply(f, (2, 3, 4))
9
apply
simply calls the passed-in function,
matching the passed-in arguments list with the function’s
expected arguments. Since the arguments list is passed in as a tuple
(a data structure), it can be computed at runtime by a program. The
real power of apply
is that it doesn’t need
to know how many arguments a function is being called with; for
example, you can use if
logic to select from a set
of functions and argument lists, and use apply
to
call any:
if <test
>:
action, args = func1, (1,)
else:
action, args = func2, (1, 2, 3)
. . .
apply(action, args)
One of the more common things programs do with lists is to apply an
operation to each node and collect the results. For instance,
updating all the counters in a list can be done easily with a
for
loop:
>>>counters = [1, 2, 3, 4]
>>> >>>updated = []
>>>for x in counters:
...updated.append(x + 10)
# add 10 to each item ... >>>updated
[11, 12, 13, 14]
Because this is such a common operation, Python provides a built-in
that does most of the work for you: the
map
function applies a passed-in function to each item in a sequence
object and returns a list containing all the function call results.
For example:
>>>def inc(x): return x + 10
# function to be run ... >>>map(inc, counters)
# collect results [11, 12, 13, 14]
Since map
expects a function, it also happens to
be one of the places where lambda
s commonly
appear:
>>> map((lambda x: x + 3), counters)
# function expression
[4, 5, 6, 7]
map
is the simplest representative of a class of
Python built-ins used for functional programming
(which mostly just means tools that apply functions to sequences).
Its relatives filter out items based on a test
(filter
) and apply operations to pairs of items
(reduce
). We say more about these built-in tools
in Chapter 8.
In Python
functions,
return
statements are optional. When a function
doesn’t return a value explicitly, the function exits when
control falls off the end. Technically, all functions return a value;
if you don’t provide a return
, your function
returns the None
object automatically:
>>>def proc(x):
...print x
# no return is a None return...
>>>x = proc('testing 123...')
testing 123... >>>print x
None
Functions such as this without a return
are
Python’s equivalent of what are called
procedures in some languages (such as Pascal).
They’re usually called as a statement (and the
None
result is ignored), since they do their
business without computing a useful result. This is worth knowing,
because Python won’t tell you if you try to use the result of a
function that doesn’t return one. For instance, assigning the
result of a list append
method won’t raise
an error, but you’ll really get back None
,
not the modified list:
>>>list = [1, 2, 3]
>>>list = list.append(4)
# append is a 'procedure' >>>print list
# append changes list in-placeNone
When you start using functions, you’re faced with choices about how to glue components together—for instance, how to decompose a task into functions, how functions should communicate, and so on. Some of this falls into the category of structured analysis and design, which is too broad a topic to discuss in this book. But here are a few general hints for Python beginners:
- Use arguments for inputs and return for outputs
Generally speaking, you should strive to make a function independent of things outside of it. Arguments and
return
statements are often the best way to isolate dependencies.- Use global variables only when absolutely necessary
Global variables (i.e., names in the enclosing module) are usually a poor way to communicate with a function. They can create dependencies that make programs difficult to change.
- Don’t change mutable arguments unless the caller expects it
Functions can also change parts of mutable objects passed in. But as with global variables, this implies lots of coupling between the caller and callee, which can make a function too specific and brittle.
Table 4.3 summarizes the ways functions can talk
to the outside world; inputs may come from items in the left column,
and results may be sent out in any of the forms on the right.
Politically correct function designers usually only use arguments for
inputs and return
statements for outputs. But
there are plenty of exceptions, including Python’s OOP
support—as we’ll see in Chapter 6,
Python classes depend on changing a passed-in mutable object. Class
functions set attributes of an automatically passed-in
self
object, to change per-object state
information (e.g., self.name
=
'bob'
); side effects aren’t dangerous if
they’re expected.
Because Python functions are objects at runtime, you can write programs that process them generically. Function objects can be assigned, passed to other functions, stored in data structures, and so on, as if they were simple numbers or strings. Function objects happen to export a special operation; they can be called by listing arguments in parentheses after a function expression. But functions belong to the same general category as other objects.
For instance, as we’ve seen, there’s really nothing
special about the name we use in a def
statement:
it’s just a variable assigned in the current scope, as if it
had appeared on the left of an =
sign. After a
def
runs, the function name is a reference to an
object; you can reassign that object to other
names and call it through any reference—not just the original
name:
>>>def echo(message):
# echo assigned to a function object ...print message
... >>>x = echo
# now x references it too >>>x('Hello world!')
# call the object by adding () Hello world!
Since 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 object by adding () ... >>>indirect(echo, 'Hello jello!')
# pass function to a function Hello jello!
You can even stuff function objects into data structures, as though they were integers or strings. Since Python compound types can contain any sort of object, there’s no special case here either:
>>>schedule = [ (echo, 'Spam!'), (echo, 'Ham!') ]
>>>for (func, arg) in schedule:
...apply(func, (arg,))
... Spam! Ham!
This code simply steps through the schedule
list,
calling the echo
function with one argument each
time through. As we hope you’re starting to notice by now,
Python’s lack of type declarations makes for an incredibly
flexible programming language. Notice the use of
apply
to run functions generically, the
single-item tuple in the second argument to apply
,
and the tuple unpacking assignment in the for
loop
header (all ideas introduced earlier).
Get Learning Python now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.