Functions 101

Lambda Expressions

Small anonymous functions can be created with the lambda keyword. This function returns the sum of its two arguments: lambda a, b: a+b.

Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression. Semantically, they are just syntactic sugar for a normal function definition. Like nested function definitions, lambda functions can reference variables from the containing scope:

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

The above example uses a lambda expression to return a function. Another use is to pass a small function as an argument:

>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

Here is an example showing implementing a compact addition function using lambda:

>>> (lambda x, y: x + y)(3, 5)
8

Decorators

A decorator is a function that creates a wrapper around another function. The primary purpose of this wrapping is to alter or enhance the behavior of the object being wrapped.

Syntactically, decorators are denoted using the special @ symbol as follows:

@decorate
def func(x):
    pass

The preceding code is shorthand for the following:

def func(x):
    pass

func = decorate(func)

In the example, a function func() is defined. However, immediately after its definition, the function object itself is passed to the function decorate(), which returns an object that replaces the original func.

Consider the following example:

from datetime import datetime
import time

def logger(func):
	def wrapper(*args, **kwargs):
		print('-' * 50)
		print(f'> Execution started {datetime.today().strftime("%Y-%m-%d %H:%M:%S")}')
		func(*args, **kwargs)
		print(f'> Execution completed {datetime.today().strftime("%Y-%m-%d %H:%M:%S")}')
		print('-' * 50)

	return wrapper

@logger
def demo_function(sleep_time):
	print('Executing task!')
	time.sleep(sleep_time)
	print('Task completed!')

demo_function(1)
demo_function(2)
demo_function(3)

Output:

Map, Filter, and Reduce

Programmers familiar with functional languages often inquire about common list operations such as map, filter, and reduce. Much of this functionality is provided by list comprehensions and generator expressions.

Python provides a built-in map() function that is the same as mapping a function with a generator expression:

>>> nums = [1, 2, 3, 4, 5]
>>> squares = map(lambda x: x * x, nums)
>>> for n in squares:
...     print(n)
... 
1
4
9
16
25

The built-in filter() function creates a generator that filters values:

>>> nums = [1, 2, 3, 4, 5]
>>> for n in filter(lambda x: x > 2, nums):
...     print(n)
... 
3
4
5

If you want to accumulate or reduce values, you can use functools.reduce(). The idea behind Python's reduce() is to take an existing function, apply it cumulatively to all the items in an iterable, and generate a single final value:

>>> from functools import reduce
>>> nums = [1, 2, 3, 4, 5]
>>> total = reduce(lambda x, y: x + y, nums)
>>> print(total)
15

Last updated