> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfnote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfnote/computer-science/python/functions-101.md).

# Functions 101

## Lambda Expressions

{% hint style="info" %}
Small **anonymous functions** can be created with the `lambda` keyword. This function returns the sum of its two arguments: `lambda a, b: a+b`.&#x20;
{% endhint %}

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:

```python
>>> 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:

```python
>>> 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:

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

## Decorators

{% hint style="info" %}
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.
{% endhint %}

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

```python
@decorate
def func(x):
    pass
```

The preceding code is shorthand for the following:

```python
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:

```python
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:

![Decorator](https://3988450783-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWVjG_njKgBtvmnKaJh%2Fuploads%2F1klh3lmQY1ZfHzbKXR9N%2Fimage.png?alt=media\&token=6a545207-1c84-4499-b8df-b60e12d33ec4)

## 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:

```python
>>> 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:

```python
>>> 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:

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