map of function list and arguments: unpacking difficulty - python

I have an assignment in a mooc where I have to code a function that returns the cumulative sum, cumulative product, max and min of an input list.
This part of the course was about functional programming, so I wanted to go all out on this, even though I can use other ways.
So I tried this:
from operator import mul
from itertools import repeat
from functools import reduce
def reduce2(l):
print(l)
return reduce(*l)
def numbers(l):
return tuple(map(reduce2, zip([sum, mul,min, max], repeat(l,4))))
l=[1,2,3,4,5]
numbers(l)
My problem is that it doesn't work. zip will pass only one object to reduce if I use it inside map, and unpacking the zip will yield the 4 tuple of (function and argument list l) so I defined reduce2 for this reason, I wanted to unpack the zip inside it but it did not work.
Python returns a TypeError: int' object is not iterable
I thought that I could use return reduce(l[0],l[1]) in reduce2, but there is still the same Error.
I don't understand the behavior of python here.
If I merely use return reduce(l), it returns again a TypeError: reduce expected at least 2 arguments, got 1
What's happening here? How could I make it work?
Thanks for your help.

Effectively, you are trying to execute code like this:
xs = [1, 2, 3, 4, 5]
reduce(sum, xs)
But sum takes an iterable and isn't really compatible with direct use via reduce. Instead, you need a function that takes 2 arguments and returns their sum -- a function analogous to mul. You can get that from operator:
from operator import mul, add
Then just change sum to add in your program.
BTW, functional programming has a variable naming convention that is really cool: x for one thing, and xs for a list of them. It's much better than the hard-to-read l variable name. Also it uses singular/plural to tell you whether you are dealing with a scalar value or a collection.

FMc answer's correctly diagnoses the error in your code. I just want to add a couple alternatives to your map + zip approach.
For one, instead of defining a special version of reduce, you can use itertools.starmap instead of map, which is designed specifically for this purpose:
def numbers(xs):
return tuple(starmap(reduce, zip([add, mul, min, max], repeat(xs))))
However, even better would be to use the often ignored variadic version of map instead of manually zipping the arguments:
def numbers(xs):
return tuple(map(reduce, [add, mul, min, max], repeat(xs)))
It essentially does the zip + starmap for you. In terms of functional programming, this version of map is analogous to Haskell's zipWith function.

Related

Making way for continuations from an apparent limitation of python reduce

Let us consider a list with numbers like the following :
a_lst = [1,2,3,2,3,4,5,6,2,2]
Now I need to write a program in python which counts the number of occurrences of let's say "2" using only "reduce" .
I went through the following question as well :
using Python reduce Count the number of occurrence of character in string
It has got a great answer , however I wanted to see if there is any way where
I could replace the "if" condition inside the lambda function with like (x == 2) . I mean getting the same thing done by not using the "if"condition explicitly .
I thought of reaching up to a solution by passing a lambda function,which takes another lambda function as an argument to the reduce function .
But it turned out to be just a day-dream and nothing else as after passing a lambda function as an argument , calling it inside the outer lambda function body will defeat the purpose of making it a lambda function .
One more fiasco was about wishing for a construct where a lambda function could call itself at the end of its body . (I understand the above line sounds completely meaningless , but what I meant a construct which had the equivalent power of a lambda calling itself )
I have been through the concept of continuation passing style ,where in pythonic terms , a function returns a lambda function which takes the arguments that the function had received .But I am not sure if by definition of continuation is technically accurate . Can it be brought to use to solve this problem ?
Theres nothing stopping you from writing
the lambda function with like (x == 2)
from functools import reduce
a_lst = [1,2,3,2,3,4,5,6,2,2]
reduce(lambda x, y: x + (y == 2), a_lst, 0) #Output: 4
The reason this works is because bool is a subclass of int in python, and can be used for mathematical operations.
If that alone however does not satisfy you, you can get really involved with the operator and functools modules. Reference docs.
from functools import reduce, partial
import operator
reduce(operator.add,map(lambda x: operator.eq(x, 2), a_lst), 0) #Output: 4
and, replace the lambda with a partial function
equals_2 = partial(operator.eq, 2)
reduce(operator.add,map(equals_2, a_lst), 0) #Output: 4
A Word of caution
It may not be wise to get stuck with one paradigm of programming (functional) in this case. Python excels in allowing any programming paradigm, but practically beats purity. It is just much simpler and easier to iterate through the list and count the number of 2s yourself, using the .count method. No need to reinvent the wheel where it doesn't make sense. To future readers, this is just a demonstration, not a recommendation on how to count occurrences in a list.

Python Function with list as argument OR multiple values as argument

I want to write a python function my_sum that extends python's built-in sum in the following way:
If a sequence is passed to my_sum it behaves like the built-in sum.
If multiple values are passed to my_sum, it returns the sum of the values.
Desired output:
my_sum([1, 2, 3]) # shall return 6 (similiar to built-in sum)
my_sum(1, 2, 3) # shall return 6 as well, (sum throws TypeError)
What worked was the following.
def my_sum(*x):
try:
return sum(x) # sums multiple values
except TypeError:
return sum(*x) # sums sequence of values
Is that the pythonic way to accomplish the desired behavior? For me the code looks odd.
It is pythonic. I think at least one check is required and python has the philosophy "Ask for forgiveness not permission" (explained here) which basically means that using try-except blocks is OK for standard control flows.
If importing an established library is pythonic and something you are allowed and willing to do, you can use numpy.sum too, as follows:
import numpy as np
def my_sum(*x):
return np.sum(x)
With this definition, both
my_sum([1, 2, 3])
my_sum(1, 2, 3)
return 6.
I think it is not an use case for exceptions, exceptions are for exceptional cases. Use another function if you convert arguments to a list and pass to the adder function.
In addition, the validation and formatting of the input should be in the outer part of the code, not in the final function. In the best case the data should be validated before coming to this function, and you should only have the sum function to deal with the cooked data.
It's a way to:
Keep things simple
Avoid adding conditional paths
Avoid defensive programming in inner code
And thus avoid future problems.
I would have the following code.
def argsToList(*x):
return list(x)
print sum([1,2,3,4])
print sum(argsToList(1,2,3,4))
# both output 10

Understanding Python Maps()

So I am trying to figure out how Python's map() function works as a way to speed up my program a little bit. From my basic understanding it looks like you can use map() to replace certain instances where you'd use a for loop. What I'm curious about is can you change something like:
loopNum = 25
for i in range (loopNum):
self.doSomething()
To:
loopNum = 25
map(self.doSomething(), range(loopNum))
Additionally, in the above example, would I be able to forego that loopNum variable, and in the map just have map(something, 25)?
No, you can't as map(function, iterable) applies function to each element of the iterable. If you simply want to execute some functionn times, just use a loop.
Note that iterable must be (surprise!) an iterable, not a number.
map is roughly the equivalent of this for loop:
# my_map(func, iter1, iterN...)
def my_map(func, *iteables)
for x, y,... in zip(iter1, iter2,iterN...):
yield func(x,y,...)
What you're doing in your code is just like this:
my_map(self.doSomething(), range(loopNum))
self.dSomething() must return a function or a callable object or this obviously doesn't work. This is because, whatever object you pass into the func argument of my_map function will be called then in addition to passing the right number of arguments to func, func must be a callable object as well :-)
*You must iterate over the iterable returned by map to obtain the results, otherwise, you would just get an iterable object without tangible work.
I want to add to this question that map() is virtually never the right tool in Python. Our BDFL himself, wanted to remove it from Python 3, together with lambdas and, most forcefully reduce.
In almost all cases where you feel tempted to use map() take a step back and try to rewrite your code using list comprehension instead. For your current example, that would be:
my_list = [self.doSomething() for i in range(loopNum)]
or
my_generator = (self.doSomething() for i in range(loopNum))
to make it a generator instead.
But for this to make any sense at all, self.doSomething() should probably take the variable i as an input. After all you are trying to map the values in your range to something else.

What is more efficient ? Using Map or For loop in python ? [duplicate]

I have a "best practices" question here. I am using map in a way that it may not be intended to be used - using the elements of a list to change the state of a different object. the final list output is not actually changed. Is this appropriate?
For example:
class ToBeChanged(object):
def __init__(self):
self.foo_lst = [1,2,3,4]
def mapfunc(self, arg):
if arg in ['foo', 'bar']:
self.foo_lst.append(arg)
else:
pass
test = ToBeChanged()
list_to_map = [1,2,37,'foo']
map(lambda x: test.mapfunc(x), list_to_map)
It is not appropriate. In Python 2, you'll be creating a new list of the same length as list_to_map, and immediately discarding it; waste! And the lambda even makes it more complicated.
Better to use a for loop:
for x in list_to_map:
test.mapfunc(x)
More concise and readable.
And if you're still thinking of using this in Python 3 (by forcing the lazy object to be evaluated in some way), consider those who will maintain your code; map gives the impression you want to create a new iterable from the list.
map is the worst.
Because if you tried to run the code in python3 it wouldn't even perform the calls since in python3 map is lazy.
In any case both calling map or a list-comprehension are expressions and expressions should be as side-effect free as possible, their purpose is to return a value.
So if you don't have a value to return you should just use the plain statements: i.e. explicit for

How to give extra arguments for the Python itertools.ifilter function?

In python I have the following function:
def is_a_nice_element(element, parameter):
#do something
return True or False
Now I would like to filter a list with this function as predicate, giving a fixed parameter. Python has the itertools.ifilter function, but I can't figure out how to pass the parameter. Is this possible? If not, how can I solve this problem?
I like functools.partial much better than lambda.
itertools.ifilter( partial(is_a_nice_element, parameter=X), iterable )
Wrap it in a lambda:
itertools.ifilter(lambda e: is_a_nice_element(e, 42), iterable)
42 is your extra argument, or whatever else you want it to be.
The solutions use lambda and functools.partial are quite correct and directly answer your question, but I don't think they are really the ideal solution.
Using filter/itertools.ifilter/map/itertools.imap with an anonymous function is prettymuch always going to be less clear than using a generator expression or list comprehension. For example, in this case I would write the generator expression
(item for item in iterable if is_a_nice_element(item, constant))
instead of using ifilter. In this case the ifilter solution is still fairly readable, but in many cases trying to go through lots of trouble to define just the right function is not going to be as worth while as just performing the operation.
If you are using ifilter then parameter would need to be constant in which case you could use a default argument parameter=something. If you want parameter to vary, you'd need to use another method to take a dyadic predicate.
If you already have the list in hand, ifilter is a bit of overkill relative to the built-in filter.
Describing what you want to accomplish would help in making a more concrete answer.

Categories