Python: Reduce() and function as argument of function - python

In Python3 I am Trying to figure out about reduce() and function as argument of function, or better passing a function as an argument of another one where the first one is not explicit see below
given:
# define a function `call` where you provide the function and the arguments
def call(y,f):
return f(y)
# define a function that returns the square
square = lambda x : x*x
# define a function that returns the increment
increment = lambda x : x+1
# define a function that returns the cube
cube = lambda x : x*x*x
# define a function that returns the decrement
decrement = lambda x : x-1
# put all the functions in a list in the order that you want to execute them
funcs = [square, increment, cube, decrement]
#bring it all together. Below is the non functional part.
#in functional programming you separate the functional and the non functional parts.
from functools import reduce # reduce is in the functools library
print(reduce(call, funcs,1)) # output 7 , 2 res 124
why it doesnt work if
I change
def call(y,f)
f(y)
in
def call(f,y)
f(y)
and gives out a error:
................py", line 27, in call
return f(y)
TypeError: 'int' object is not callable

functools.reduce()
To understand this, we should first understand how reduce works, reduce takes 3 arguments:
A function
An iterable element
An initializer.
Let us focus on the function and iterable element to understand how function is called
Below is the official docs of functools:
functools.reduce(function, iterable[, initializer])
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
Here you could understand that, it takes the function passed in the first argument and executes it with value, element as arguments for the function passed. Note that element is each element in the second argument iterable. So when you called reduce(call, funcs, 1),
The following happened:
Since initializer=1, value=initializer,
for every func in funcs, the following happened
call(1,func)
TLDR;
When you replace y and f, you are trying to call 1(func) which is not possible and that is why the first initial solution works because it calls func(1)
Reference: Python Docs - functools

Related

What is the point of using *args when a list of arguments can be used?

Would passing in a list or dictionary of variables be more concise than passing in *args in Python methods?
For example,
def function(a, *argv):
print('First variable:', a)
for k in argv:
print('Additional variable:',k)
is the same as
def function(a, list):
print('First variable:', a)
for k in list:
print('Additional variable:',k)
except a list is passed in the second argument. What I think using *args would often do is to cause additional bugs in the program because the argument length only needs to be longer than the mandatory argument length. Would any please explain situations where *args would be really helpful? Thanks
The first function accepts:
function('hello', 'I', 'am', 'a', 'function')
The second one won't. For the second you'd need:
function('hello', ['I', 'am', 'a', 'function'])
In principle, the first one is used when your function can have an arbitrary number of parameters (think: print), while the second one specifies that there's always a second parameter, which is an iterable (not necessarily a list, despite the name)
Passing *args is useful when you have to extract only some (or none) arguments in first level function and then pass others to other inner function without knowing about the details. e.g.
def inner_func(x, y, z, *args, **kwargs):
# do something with x, y, and z
return output
def outer_func(a, b, *args, **kwargs):
# do something with a and b
# pass the rest arguments to inner function without caring about details
output = inner_func(*args, **kwargs)
# do something with output
return
That is a fair ask as to why *args (or **kwargs) is essentially required when a list (or dict) could do the same task. The key reason to that is when a ** caller of a function does not know the number of arguments beforehand**. I'll try to explain this with reference to the particular scenario you have shared.
Lets suppose that we have the below function which finds the sum of all integers passed in. (I'm giving up sum builtin function for demonstration purpose, please bear with me :) )
def do_add(*int_args):
total = 0
for num in int_args:
total += num
return total
And you want to call this for an unknown number of arguments with an unknown number of times.
If in case you need to send a list argument, the do_add function might look like below:
def do_add(int_list):
total = 0
for num in int_list:
total += 0
return total
l1 = [1, 2, 3, 4, ... n] # memory loaded with n int objects
do_add(l1)
l2 = [10, 20, ... n]
do_add(l2)
Firstly, you are loading the memory with an additional list object created just for the sake of function call. Secondly, if you have to add some more items to the list we may need to call another list method such as append or extend.
But if you follow the *args approach, you can avoid creating an extra list and focus only on the function call. If you need to add more arguments you can just add another argument separated by a comma rather than calling append or extend methods.
Assume that you want to call this function n times with 1000 arguments. It will result in n * 1000 new list objects to be created every time. But with the variable arguments approach, you can just call it directly.
do_add(1, 2, 3) # call 1
do_add(10.0, 20.0, 30.0) # call 2
...
do_add(x, y, z, ..., m) # call n

How to take the logical and of a list of boolean functions in python

Say I have a list of functions li = [fun1, fun2, ..., funk] that take one argument and return a boolean. I'd like to compose them into a single function that returns the logical and of the return values of each of the fun's when evaluated at its argument. (in other words I'd like to have fun(x) = fun1(x) and fun2(x) and ... and funk(x).)
Is there an elegant way of doing this?
Use all to create the composite function
def func(x, lst):
#lst is the list of functions and x is used as an argument for functions
return all(fun(x) for fun in lst)
And then call it as
func(x,[fun1, fun2, fun3,...., funk])
If a lambda function is needed, you can do the following, however it is against PEP-8 guidelines
Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier.
func = lambda x:all(fun(x) for fun in lst)
And call it as
func(x)
all will do it
all(func(x) for func in li)

int object is not iterable python

I am trying to learn python reduce function.
This is some code that does not make sense to me
>>> x = [1,2,3]
>>> reduce(sum, [b for b in x if b > 0])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> reduce(sum, [b for b in x if b > 2])
3
Why does it work when b > 2 but not b > 0
The code seems almost exactly the same
In the second case the list you created with list comprehension has only one element, so it is not reduced. This reduction doesn't fail, because there is nothing to be reduced there, so function sum isn't executed and it doesn't fail. The result is the only integer, that has been there before.
The function sum is defined in the documentation. As you can see, it is supposed to sum values in some iterable container, such as list or tuple. You could write sum([1,2,3]) and it would work just like this reduction, that you want to make. If you want to achieve this result with using the reduction function, you have to make sure, that you have a function, that takes two integers and returns their sum.
Here you have such function:
def sum2(x, y):
return x + y
Reduce with such function should give you expected results.
sum2 = lambda x, y: x+y
The following function does the same, but is shorter and nicer due to lambda notation, it might be nice to look at it sometime in the future, if you don't know it yet.
From the docs about reduce:
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
So in the first case, you have used reduce wrongly by using the builtin sum which does not take two arguments but an iterable. sum consumes the iterable in the first iteration of reduce and returns an int which is not further reduceable.
In the second case, sum is not even called on the iterable:
If initializer is not given and iterable contains only one item, the
first item is returned.
Since iterable is of length 1, reduce returns the one item in the iterable and sum does not nothing.
Input in second code snippet is single element list - it's a degenerated case where callable isn't even called.
Consider following example:
def my_sum(*args):
print "my_sum called with", args
return sum(*args)
x = [1, 2, 3]
reduce(my_sum, [b for b in x if b > 2])
# nothing gets printed, value 3 is returned, since it's only element in list
Now consider your failed case:
reduce(my_sum, [b for b in x if b > 0])
Output is:
my_sum called with (1, 2)
[exception traceback]
Equivalent call to builtin sum is sum(1, 2), which results in same exception as quoted by you.
sum signature does not follow rules for reduce function. sum(iterable[, start]) takes two arguments (as expected), but first of them has to be iterable, where second is optional initial value.
reduce requires function to take two arguments, where (quoting docs):
The left argument, x, is the accumulated value and the right argument,
y, is the update value from the iterable.
We can clearly see that these interfaces are not conform with each other. Correct callable will be something like:
def reduce_sum(accumulator, new_value):
return accumulator + new_value
reduce(reduce_sum, [b for b in x if b > 0])
As Moses points out "sum consumes the iterable in the first iteration"
so let's play with it
>>> sum(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> sum([1,2])
3
>>> sum(x)
6
>>> sum([b for b in x if b >0])
6
>>> reduce(lambda x, y: x+y, x)
6
>>> reduce(lambda x, y: x+y, [b for b in x if b > 0])
6
>>> reduce(lambda x, y: x+y, [b for b in x if b > 1])
5
The reduce function takes in a function and an iterable.
It then maps each element of that iterable to the function.
What your first b > 0 function is doing is trying to run sum with 2 arguments. One argument being the accumulated total, and the other being an element of the input list, for example it would look like: sum(0, 1).
This is stated in the documentation as:
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. ... The left argument, is the accumulated value and the right argument, is the update value from the iterable.
What your second b > 2 function is doing is simply running the reduce function with an iterator containing one item, which does not attempt to apply sum as stated in the documentation.
If initializer is not given and iterable contains only one item, the first item is returned.
But since sum takes in an iterable, you could just do:
sum([b for b in x if b > 0])

python calling function twice one after another but second storing value from first

I am using python so can someone tell me how to call same function twice in python but when you call it the second time it should be changed an already have value stored in it when you called the function the first time, so I basically mean you are calling the function first time and right after that you are calling it again but with the return value from the first time you called that function.
Assuming you have a function that has both a parameter and a return value:
def myFunction(input):
# do something with input
return input
To have a second instance of the function use the result of the first instance, you can simply nest the functions:
result = myFunction(myFunction(value))
you can create function that will apply n-times
def multf(f, n, x):
if n == 0:
return x
return multf(f, n-1, f(x))
so here we apply lambda sqr 3 times, it becomes f(f(f(x)))
sqr = lambda x: x**2
print(multf(sqr,3,2))
256

Which object does name "g" bind to?

The below assignment is taken from here:
Q5. Define the repeated function from Homework 2 by calling reduce with compose1 as the first argument. Add only a single expression to the starter implementation below:
def square(x):
return x*x
def compose1(f, g):
"""Return a function of x that computes f(g(x))."""
return lambda x: f(g(x))
from functools import reduce
def repeated(f, n):
"""Return the function that computes the nth application of f, for n>=1.
f -- a function that takes one argument
n -- a positive integer
>>> repeated(square, 2)(5)
625
>>> repeated(square, 4)(5)
152587890625
"""
assert type(n) == int and n > 0, "Bad n"
return reduce(compose1, "*** YOUR CODE HERE ***" )
To complete this assignment, I would like to understand, to what does g bind to? f binds to the square function.
First, what should repeated(f, 4) return?
A function that, when called on some arbitrary arg, will return f(f(f(f(arg)))).
So, if you want to build that with compose1, you'll need to return either compose1(compose1(compose1(f, f), f), f) or compose1(f, compose1(f, compose1(f, f))).
Now, look at what reduce does, and figure out what it's going to pass to compose1 each time. Clearly your iterable argument has to either start or end with f itself. But what else do you want there to make sure you get one of the two acceptable results?
And meanwhile, inside each call to compose1 except the last, one of the two arguments has to be the repeated function's f, while the other will be the result of another call to compose1. (The last time, of course, they'll both be f.) Figure out which of those is f and which is g, and how you get reduce to pass the right values for each, and you've solved the problem.

Categories