Functions
A function is a block of code whose location in memory is bound to a name.
The code can be executed by calling the function.
To call a function, write its name followed by a set of parentheses.
Functions (or, more accurately, callables) are objects which define a
__call__
method.
When called, the __call__
method is executed.
Functions are created using the def
statement.
A function can return a value to its caller using the return
keyword.
A function can return multiple values by using tuple packing.
If a function does not explicitly return a value, it automatically returns None
.
Scope
Each function has its own scope. Names bound in the function’s body are in the function’s scope. Names bound outside of any function are in the module scope. When a name is referenced in a function, the interpreter searches for the name in the function’s scope first, then the scopes of any enclosing functions, then the module scope.
In this example, the interpreter searches for x
in foo
’s scope, then the
module scope.
Initially, x
is not bound to a value, so NameError
is raised.
After binding x
to a value (in the module scope), foo
works.
Here, foo
binds x
in its local scope, so x
refers to its local value.
The module’s x
is unaffected.
When a name is bound in a function, that name always references the local value.
A function can refer to and rebind a global variable by using the global keyword.
Note that using global
is often an indicator of poor design.
Functions should only modify local variables whenever possible.
There is also the nonlocal keyword. This is used by nested function definitions to refer to a name in an enclosing function’s scope, but not a name in the module scope.
Parameters and Arguments
A function can specify certain local names which are to be bound by the caller
of the function.
These are called parameters and are listed between the parentheses in the
def
statement.
The caller of the function can specify the values of the parameters, called
arguments, between the parentheses in the call.
In this example, foo
’s local name x
is bound to the module name x
in the
first call, the value 3
in the second call, and the value 'hello'
in the
third call.
Using a name as an argument to a function will cause the function’s local name
to refer to the same memory location as the argument.
This is called pass by pointer or pass by object reference in other
languages.
This means that, although re-binding a parameter is not visible outside of the
function, modifying mutable values such as list
or dict
objects is visible
to the caller.
Function parameters can have default values.
Note that default values are evaluated only once, and the same object is
shared between calls.
This means that if the default value is mutable (such as a list
or dict
),
any modifications to it in one call will be present in subsequent calls.
If you want the default to be a list
or dict
, you probably want this:
Since bool(None)
evaluates to False
, you might see code like this:
L = L or []
.
However, this can lead to surprising results.
Empty sequences are also False
, so if the caller supplies a list which happens
to be empty, this code will create and modify a new list, where the caller
expected the function to modify the supplied list.
To support modifying a supplied, empty list, you should explicitly compare the
parameter to None
: if L is None: L = []
Arguments to functions can be passed by keyword, by specifying the parameter name and its value.
(note that there is a hypot
function in the math
module)
If a parameter name is prefixed with *
, then that parameter will be a
tuple
containing any extra positional arguments.
Normally, these “variadic” arguments will be last in the list of parameters,
because they scoop up all remaining input arguments that are passed to the
function. Any parameters which occur after the *args
parameter are
keyword-only arguments, meaning that they can only be used as keywords
rather than positional arguments.
A single *
indicates that all subsequent parameters are keyword-only,
without collecting the extra positional arguments in a tuple
.
If a parameter name is prefixed with **
, then that parameter will be a
dict
containing any extra keyword arguments.
If present, a **
parameter must follow any *
parameter.
Function arguments can be stored in a tuple
or list
and unpacked using *
.
Function arguments can be stored in a dict
and unpacked using **
.
Lambdas
The keyword lambda
is used to create small functions.
The body of a lambda can only contain one expression, whose value is returned.
If a lambda returns a tuple
, it must be parenthesized.
Here, the first line tries to create a tuple
containing a lambda
and the
expression x ** 2
.
This is demonstrated better by lines 2 and 3: foo
is a tuple
containing
the lambda
and 5
.
Enclosing the body of the lambda in parentheses works as intended,
creating a lambda
which returns a tuple
.
Note that while def
is a statement, lambda
is an expression.
This means that a lambda
can appear in a larger expression, such as a function
call or tuple
, list
, or dict
literal.
Higher-Order Functions
A “higher-order” function is one which treats other functions as data. This means that a higher-order function may accept other functions as parameters or return new functions. Since Python’s functions are just objects bound to names, a function can be passed to another function just by passing its name. It is also common to pass a lambda to a higher-order function.
A simple higher-order function is apply
, which takes a function and an
argument and returns the result of passing the argument to the function.
Three common higher-order functions are map
, filter
, and reduce
.
These are used to process sequences, and can often replace for
loops.
Python’s implementations can accept any iterable.
The map
function takes a function and an iterable as arguments and returns an
iterator whose __next__
method returns the result of applying the function
to the next item in the sequence.
Python’s map
can take multiple iterables, as long as the number of iterables
matches the number of parameters of the given function.
The filter
function takes a predicate function and an iterable.
A predicate function takes one parameter and returns a bool
.
filter
returns an iterator over the items for which the predicate returns
True
.
The reduce
function reduces an iterable to a single value.
It takes three values: a function, an iterable, and an initialized accumulator.
The function must take two parameters, the accumulator and the current item,
and returns an updated accumulator.
reduce
returns the final accumulator.
In Python 2, reduce
was a built-in function; in Python 3, reduce
has been
moved to the functools
module.
If the initial accumulator is omitted, the first item in the sequence is used.
There are other higher-order functions in the itertools and functools modules. The operator module contains functions that represent Python’s operators, for use with higher-order functions.
Generators
A generator
is an object which looks like a function but behaves like an iterator.
A generator is defined like a function, except that the yield
keyword is used
instead of the return
keyword.
When called, the generator returns an iterator whose __next__
method executes
code in the generator until it encounters a yield
expression.
The argument to the yield
expression is returned from the __next__
method.
When the generator returns, the __next__
method raises StopIteration
.
A simple generator which mimics Python 2’s xrange
might look like this:
Generators can be used to create iterators with infinite size while using
constant memory.
The following fib
generator generates the Fibonacci sequence without bound
while only using enough memory to store a
and b
.
The yield
expression can also delegate to a sub-generator using yield from
.
Generators can also be used for lightweight coroutines, see PEP 342.
See the documentation for yield.
Generator Expressions
Like lambdas, generator expressions are a syntax for creating simple generators. A generator expression is a comprehension enclosed in parentheses instead of brackets.
Note that generator expressions can be used in exactly the same way as the
built-in functions map
and filter
.
It is mostly a matter of personal preference as to which is more readable.
In terms of speed, generator expressions are slower than map
but faster than
filter
.
Some simple results using timeit: