The folding concept opens the doors to build many other functions. It allows to be done without having recourse to writing explicit recursive code or managing loops. For example, max , min , sum , prod , any , all , map , filter among others, can all be defined with folding/reduce functions.
Functional programming wants to avoid state changes as much as possible and works with data flowing between functions. In Python you might combine the two approaches by writing functions that take and return instances representing objects in your application (e-mail messages, transactions, etc.).
In high order function, a function can act as an instant of an object type. In high order function, we can return a function as a result of another function. In high order function, we can pass a function as a parameter or argument inside another function.
The Pythonic way of summing an array is using sum
. For other purposes, you can sometimes use some combination of reduce
(from the functools
module) and the operator
module, e.g.:
def product(xs):
return reduce(operator.mul, xs, 1)
Be aware that reduce
is actually a foldl
, in Haskell terms. There is no special syntax to perform folds, there's no builtin foldr
, and actually using reduce
with non-associative operators is considered bad style.
Using higher-order functions is quite pythonic; it makes good use of Python's principle that everything is an object, including functions and classes. You are right that lambdas are frowned upon by some Pythonistas, but mostly because they tend not to be very readable when they get complex.
Haskell
foldl (+) 0 [1,2,3,4,5]
Python
reduce(lambda a,b: a+b, [1,2,3,4,5], 0)
Obviously, that is a trivial example to illustrate a point. In Python you would just do sum([1,2,3,4,5])
and even Haskell purists would generally prefer sum [1,2,3,4,5]
.
For non-trivial scenarios when there is no obvious convenience function, the idiomatic pythonic approach is to explicitly write out the for loop and use mutable variable assignment instead of using reduce
or a fold
.
That is not at all the functional style, but that is the "pythonic" way. Python is not designed for functional purists. See how Python favors exceptions for flow control to see how non-functional idiomatic python is.
Starting Python 3.8
, and the introduction of assignment expressions (PEP 572) (:=
operator), which gives the possibility to name the result of an expression, we can use a list comprehension to replicate what other languages call fold/foldleft/reduce operations:
Given a list, a reducing function and an accumulator:
items = [1, 2, 3, 4, 5]
f = lambda acc, x: acc * x
accumulator = 1
we can fold items
with f
in order to obtain the resulting accumulation
:
[accumulator := f(accumulator, x) for x in items]
# accumulator = 120
or in a condensed formed:
acc = 1; [acc := acc * x for x in [1, 2, 3, 4, 5]]
# acc = 120
Note that this is actually also a "scanleft" operation as the result of the list comprehension represents the state of the accumulation at each step:
acc = 1
scanned = [acc := acc * x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 2, 6, 24, 120]
# acc = 120
In Python 3, the reduce
has been removed: Release notes. Nevertheless you can use the functools module
import operator, functools
def product(xs):
return functools.reduce(operator.mul, xs, 1)
On the other hand, the documentation expresses preference towards for
-loop instead of reduce
, hence:
def product(xs):
result = 1
for i in xs:
result *= i
return result
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With