20

Is there any clean way to apply a list of functions on an object in Python without lambda or list comprehensions? Like the Haskell expression:

map ($ obj) [foo1,foo2]

Example with lambda in Python:

response = map(lambda foo:foo(obj),[foo1,foo2]) #fooX:object->Bool

Is it extendable to class functions?

Perhaps something from operator or itertools?

10
  • 2
    Is there a reason to avoid list comprehensions, e.g. [f(obj) for f in [foo,foo2]]? What do you want to do with the return values from the functions? Commented Jul 31, 2012 at 8:55
  • I just think they are somewhat ugly and was wondering if there was any alternatives, the code is at the moment a list comprehension :) Commented Jul 31, 2012 at 9:03
  • @SlimJim No it's not ugly! That is perfectly fine python code. Commented Jul 31, 2012 at 9:05
  • 1
    @SlimJim: List comprehensions were borrowed from Haskell, a pure functional language - map/filter are not more functional than list comprehensions. Commented Jul 31, 2012 at 9:21
  • @SlimJim: So I think the summary is: No you can't do what you need without either map and lambda or list comprehensions. You can pick whichever one you want, but there isn't any "clean" alternative (and the functional style may be discouraged by some as "unpythonic"). Commented Jul 31, 2012 at 9:23

7 Answers 7

19

You could always just create a function to take care of it for you:

def map_funcs(obj, func_list):
    return [func(obj) for func in func_list]

    # I was under the impression that the OP wanted to compose the functions,
    # i.e. f3(f2(f1(f0(obj))), for which the line below is applicable:
    # return reduce(lambda o, func: func(o), func_list, obj)


map_funcs(it, [Buy, Use, Break, Fix])
Sign up to request clarification or add additional context in comments.

8 Comments

Alas, break is a keyword and can't be used as a function name.
@Blckknght Thank God for case sensitive object names. Also, a more crucial ninja edit to actually do what the questioner wants.
Should have been reduce(lambda o, func: func(o), func_list, obj) btw.
I don't know if OP wants f3(f2(f1(obj))) or [f1(obj), f2(obj), f3(obj)]. I don't know Haskell and hence what Haskell's map ($ obj) [foo1,foo2] does. map intuitively should return another list of elements (i.e. [f1(obj), f2(obj), f3(obj)]), but if what OP wants is function composition, then the commented reduce code is what's sought after.
@ladaghini If you look at the python equivalent you know that it's [f1(obj), f2(obj), f3(obj)]
|
12

I think this should fit your 'functional' criteria, To answer your question, I don't think there is a clean way and you should just acclimatize to list comprehensions.

As suggested by @J.F.Sebastian

>>> from operator import methodcaller
>>> funcs = (lambda x: x + 1, lambda x: x + 2)
>>> obj = 5
>>> list(map(methodcaller('__call__', obj), funcs))
[6, 7]

Here is a crazy way of doing it:

>>> from itertools import starmap, repeat
>>> from types import FunctionType
>>> funcs = (lambda x: x + 1, lambda x: x + 2)
>>> obj = 5
>>> list(starmap(FunctionType.__call__, zip(funcs, repeat(obj))))
[6, 7]

As suggested by @AleksiTorhamo

>>> from itertools import repeat
>>> from types import FunctionType
>>> obj = 5
>>> funcs = (lambda x: x + 1, lambda x: x + 2)
>>> list(map(FunctionType.__call__, funcs, repeat(obj)))
[6, 7]

8 Comments

+1 for the crazy library function usage in order to avoid lambdas (and then for throwing a few in anyway, in the setup code).
from the question: "Is there any clean way". Who on earth would think that the above is a clean code?
it is awful. There is no excuse. Even if you would use apply. It belongs at best in BBCC e.g., Best way to increment of 1 in python?. btw, It is not the only awful way: map(methodcaller('__call__', obj), func_list)
@J.F.Sebastian Community wikied it since your way is much better.
You can use operator.call instead of FunctionType.__call__ in python3.11 and further.
|
9

The problem is the missing $ operator which is trivially defined by

def apply(f, a):
    return f(a)

then one can do the currying ($ obj) with a partial in python like this: partial(apply, a=obj)

having this we can do a map apply with

map(partial(apply, a=obj), [foo1, foo2]))

1 Comment

Also, as J. F. Sebastian mentioned above, ($ x) in Haskell can be implemented using library functions in Python as operator.methodcaller('__call__', x), though it certainly doesn't look that pretty. Because of this, defining an apply function as you have might be a better choice for readability.
5

I think that list comprehensions are the best way to build one list based on another. Applying regular functions from a list is quite easy:

results = [f(obj) for f in funcList]

If you don't need the whole list of results at once, but only need to iterate over the items in one at a time, a generator expression may be better:

genexp = (f(obj) for f in funcList)
for r in genexp:
    doSomething(r)

If your functions are methods, rather than basic functions there are two ways to go:

Using bound methods, in which case you don't need to provide the object at all when making the function calls:

obj = SomeClass()
funcList = [obj.foo1, obj.foo2]
results = [f() for f in funcList]

Or using unbound methods, which are simply regular functions that expect an instance of the class they are defined in as their first argument (conventionally named self):

funcList = [SomeClass.foo1, SomeClass.foo2]
obj = SomeClass()
results = [f(obj) for f in funcList]

Of course, if you don't need to capture the results of the function, it is simplest to simply write a loop:

for f in funcList:
    f(obj)

Comments

1

The library toolz has a compose function that does just that.

from toolz import compose
compose(foo1, foo2)(obj)

Comments

1

I think the question can be read in two ways, apply a list of functions to an object and yield the result of each in a list (Single input, multiple output) OR apply a list of function to an object in sequence (Singe input, single output).

The latter is solved by the compose function, or by defining your own:

def compz(t, funcs):
  return t if not funcs else compz(funcs[0](t), funcs[1:])

print(compz("str", [lambda x: x+"A", lambda x: x+"B", lambda x: x+"C"]))
print(compz("Hello, World", [lambda x: f"<h1>{x}</h1>", lambda x: f"<body>{x}</body>", lambda x: f"<html>{x}</html>",]))
print(compz(2, [lambda x: x+2, lambda x: x+5, lambda x: x+7]))
print(compz(2, [lambda x: x**2, lambda x: x**2, lambda x: x**2]))
print(compz(2, [lambda x: x**2, lambda x: x**2]))
print(compz(2, [lambda x: x**2]))
print(compz(2, []))

strABC
<html><body><h1>Hello, World</h1></body></html>
16
256
16
4
2

Most answers to the latter are equivalent, but really the list comprehension either because the functions are applied in "parallel" not in sequence or because you have to import a separate library. looking at a few answer, it can depend on what your result should look like, i.e. are you okay to have

func_list = [lambda x: x+"A", lambda x: x+"B", lambda x: x+"C"]

print([f("str") for f in func_list])
print([f for f in map(lambda f: f("str"), func_list)])

from functools import partial

def apply(f, a):
    return f(a)
  
print([f for f in map(partial(apply, a="str"), func_list)])

def map_funcs(obj, func_list):
    return [func(obj) for func in func_list]

print(map_funcs("str", func_list))

In all those cases the result is:

['strA', 'strB', 'strC']

Comments

0

This is my solution:

def plus(i):
    return i+i

def mult(i):
    return i*4

functions = [plus,mult]

result=[]

for i in ["a","b","c","d"]:
    for j in functions:
        result.append(j(i))

result Out[16]: ['aa', 'aaaa', 'bb', 'bbbb', 'cc', 'cccc', 'dd', 'dddd']

2 Comments

I think this doesn't answer the question. In the question there is only one object and a list of functions, but here you have both a list of functions and a list of objects.
For one object called i, result=[] for j in functions: result.append(j(i))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.