11.3. FP Apply Reduce
Reduce sequence using function
Built-in
In Python, reduce()
is a built-in function that applies a given function
to the elements of an iterable (e.g. a list, tuple, or set) and returns a
single value. The function takes two arguments: the first is the function to
apply, and the second is the iterable to be processed.
The reduce()
function works by applying the function to the first two
elements of the iterable, then to the result and the next element, and so
on, until all elements have been processed and a single value is obtained.
Here's an example:
>>> from functools import reduce
>>>
>>> # Define a list of numbers
>>> numbers = [1, 2, 3, 4, 5]
>>>
>>> # Use reduce() to sum the numbers
>>> result = reduce(lambda x,y: x+y, numbers)
>>>
>>> print(result)
15
In this example, the reduce()
function applies the lambda
function
(which adds two numbers) to the elements of the numbers
list, resulting
in the sum of all the numbers.
11.3.1. SetUp
>>> from functools import reduce
11.3.2. Syntax
functools.reduce(function, iterable[, initializer])
required
callable
- Functionrequired
iterable
- Sequence or iterator object
11.3.3. Problem
>>> def add(x, y):
... return x + y
>>>
>>>
>>> DATA = [1, 2, 3, 4]
>>> result = 0
>>>
>>> for element in DATA:
... result = add(result, element)
>>>
>>> print(result)
10
11.3.4. Solution
>>> DATA = [1, 2, 3, 4]
>>>
>>>
>>> def add(x, y):
... return x + y
>>>
>>> reduce(add, DATA)
10
11.3.5. Rationale
The operator
module in Python provides a set of functions that implement
common operations on Python objects, such as arithmetic operations,
comparisons, and logical operations. These functions are designed to be used
as functional arguments to other functions, such as map()
, filter()
,
and reduce()
.
The operator
module provides functions for arithmetic operations such as
addition, subtraction, multiplication, division, and exponentiation. It also
provides functions for bitwise operations, such as bitwise AND, OR, XOR, and
shift operations.
In addition to arithmetic and bitwise operations, the operator
module
provides functions for comparisons, such as greater than, less than, equal
to, and not equal to. It also provides functions for logical operations,
such as not
, and
, and or
.
Here's an example of using the operator
module to sort a list of tuples
based on the second element of each tuple:
>>> import operator
>>>
>>> # Define a list of tuples
>>> data = [(2, 'b'), (1, 'a'), (3, 'c')]
>>>
>>> # Sort the list based on the second element of each tuple
>>> sorted_data = sorted(data, key=operator.itemgetter(1))
>>>
>>> print(sorted_data)
[(1, 'a'), (2, 'b'), (3, 'c')]
In this example, the itemgetter()
function from the operator
module
is used as the key
argument to the sorted()
function. This function
returns a callable that extracts the second element of each tuple, which is
used to sort the list.
>>> DATA = [1, 2, 3, 4]
>>>
>>> from operator import mul
>>> reduce(mul, DATA)
24
>>> def add(a, b):
... print(f'{a=}, {b=}')
... return a + b
>>>
>>>
>>> data = [1, 2, 3, 4]
>>> reduce(add, data)
a=1, b=2
a=3, b=3
a=6, b=4
10
11.3.6. Use Case - 1
>>> from functools import reduce
>>>
>>> data = (1, 2, 3, 4)
>>> def add(x, y):
... return x + y
>>>
>>> def sub(x, y):
... return x - y
>>>
>>> def mul(x, y):
... return x * y
>>>
>>>
>>> reduce(add, data)
10
>>> reduce(sub, data)
-8
>>> reduce(mul, data)
24
>>> reduce(lambda x,y: x+y, data)
10
>>> reduce(lambda x,y: x-y, data)
-8
>>> reduce(lambda x,y: x*y, data)
24
>>> from operator import add, sub, mul
>>>
>>> reduce(add, data)
10
>>> reduce(sub, data)
-8
>>> reduce(mul, data)
24
11.3.7. Use Case - 2
>>> from functools import reduce
>>> from operator import add
>>> data = [
... [1, 2, 3],
... [4, 5, 6],
... [7, 8, 9],
... ]
>>> result = 0
>>>
>>> for row in data:
... for digit in row:
... result += digit
>>>
>>> print(result)
45
>>> sum(data[0])
6
>>>
>>> sum(data[1])
15
>>>
>>> sum(data[2])
24
>>>
>>>
>>> sum(data[0]) + sum(data[1]) + sum(data[2])
45
>>> reduce(add, data[0])
6
>>>
>>> reduce(add, data[1])
15
>>>
>>> reduce(add, data[2])
24
>>>
>>>
>>> reduce(add, (
... reduce(add, data[0]),
... reduce(add, data[1]),
... reduce(add, data[2]),
... ))
45
11.3.8. Use Case - 3
>>> from functools import reduce
>>> from itertools import starmap
>>> from operator import add, sub, mul
>>> def square(x):
... return x ** 2
>>>
>>> def cube(x):
... return x ** 3
>>>
>>> def apply(data, fn):
... return map(fn, data)
>>> data = (1, 2, 3, 4)
>>> funcs = (square, cube)
>>>
>>> result = reduce(apply, funcs, data)
>>> tuple(result)
(1, 64, 729, 4096)
>>>
>>> result = reduce(apply, funcs, data)
>>> reduce(add, result)
4890
>>> data = (1, 2, 3, 4)
>>> funcs = (add, sub, mul)
>>>
>>> result = map(lambda fn: reduce(fn,data), funcs)
>>> reduce(add, result)
26
>>> funcs = (
... (add, data),
... (sub, data),
... (mul, data),
... )
>>>
>>> data = (1, 2, 3, 4)
>>> result = starmap(reduce, funcs)
>>> reduce(add, result)
26
>>> result = starmap(reduce, (
... (add, data),
... (sub, data),
... (mul, data),
... ))
>>>
>>> data = (1, 2, 3, 4)
>>> reduce(add, result)
26
11.3.9. Use Case - 4
split-apply-combine strategy
Apply function of two arguments cumulatively to the items of iterable, from
left to right, so as to reduce the iterable to a single value. For example,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
calculates
((((1+2)+3)+4)+5)
. The left argument, x, is the accumulated value and
the right argument, y, is the update value from the iterable. If the
optional initializer is present, it is placed before the items of the
iterable in the calculation, and serves as a default when the iterable is
empty. If initializer is not given and iterable contains only one item, the
first item is returned.
Roughly equivalent to:
>>> def reduce(function, iterable, initializer=None):
... it = iter(iterable)
... if initializer is None:
... value = next(it)
... else:
... value = initializer
... for element in it:
... value = function(value, element)
... return value
SetUp:
>>> from functools import reduce
>>>
>>> DATA = (1, 2, 3, 4, 5)
Usage:
>>> def add(a, b):
... return a + b
>>>
>>> reduce(add, DATA)
15
>>> reduce(lambda x,y: x+y, DATA)
15
11.3.10. Use Case - 5
>>> def square(x):
... return x ** 2
>>>
>>> def cube(x):
... return x ** 3
>>>
>>> def apply(data, fn):
... return map(fn, data)
>>>
>>> def add(x, y):
... return x + y
>>> data = (1, 2, 3, 4)
>>> transformations = [square, cube]
>>> result = reduce(apply, transformations, data)
>>> list(result)
[1, 64, 729, 4096]
>>> result = reduce(apply, transformations, data)
>>> reduce(add, result)
4890
11.3.11. Assignments
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Functional Reduce Chain
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3
# %% English
# 1. Define `result` as a `range()` from 0 (inclusive) to 10 (exclusive)
# 2. Map `results` using `square` function
# 3. Filter `results` using `even` function
# 4. Reduce `results` using `product` function
# 5. Run doctests - all must succeed
# %% Polish
# 1. Zdefiniuj `result` jako `range()` od 0 (włącznie) do 10 (rozłącznie)
# 2. Przemapuj `result` używając funkcji `square`
# 3. Przefiltruj `result` używając funkcji `even`
# 4. Zredukuj `result` używając funkcji `product`
# 5. Uruchom doctesty - wszystkie muszą się powieść
# %% Hints
# - `range()`
# - `map()`
# - `filter()`
# - `reduce()`
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> assert isfunction(even), \
'Object `even` must be a function'
>>> assert isfunction(square), \
'Object `square` must be a function'
>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'
>>> assert type(result) is int, \
'Variable `result` has invalid type, should be int'
>>> result
147456
"""
from functools import reduce
def even(x):
return x % 2 == 0
def square(x):
return x ** 2
def product(x, y):
return x * y
# Define `result` as a `range()` from 0 (inclusive) to 10 (exclusive)
# type: range
result = ...
# Map `results` using `square` function
# type: map
result = ...
# Filter `results` using `even` function
# type: filter
result = ...
# Reduce `results` using `product` function
# type: int
result = ...
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`
# %% About
# - Name: Iterator Reduce Impl
# - Difficulty: medium
# - Lines: 5
# - Minutes: 13
# %% English
# 1. Write own implementation of a built-in `reduce()` function
# 2. Define function `myreduce` with parameters:
# - parameter `function: Callable`
# - parameter `iterable: list | tuple`
# 3. Don't validate arguments and assume, that user will:
# - always pass valid type of arguments
# - iterable length will always be greater than 0
# 4. Do not use: map, filter, zip, enumerate, all, any, reduce
# 5. Run doctests - all must succeed
# %% Polish
# 1. Zaimplementuj własne rozwiązanie wbudowanej funkcji `reduce()`
# 2. Zdefiniuj funkcję `myreduce` z parametrami:
# - parameter `function: Callable`
# - parameter `iterable: list | tuple`
# 3. Nie waliduj argumentów i przyjmij, że użytkownik:
# - zawsze poda argumenty poprawnych typów
# - długość iterable będzie większa od 0
# 4. Nie używaj: map, filter, zip, enumerate, all, any, reduce
# 5. Uruchom doctesty - wszystkie muszą się powieść
# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'
>>> from inspect import isfunction
>>> from operator import add, mul
>>> assert isfunction(myreduce)
>>> myreduce(add, [1, 2, 3, 4, 5])
15
>>> myreduce(mul, [1, 2, 3, 4, 5])
120
"""
# Write own implementation of a built-in `reduce()` function
# Define function `myreduce` with parameters: `function`, `iterable`
# type: Callable[[Callable, Iterable], Any]
def myreduce(function, iterable):
...