52

I need to do something that is functionally equivalent to this:

for foo in foos:
    bar = foo.get_bar()
    # Do something with bar

My first instinct was to use map, but this did not work:

for bar in map(get_bar, foos):
    # Do something with bar

Is what I'm trying to accomplish possible with map? Do I need to use a list comprehension instead? What is the most Pythonic idiom for this?

2
  • 4
    Why wouldn't you just use a list comprehension? Commented Oct 13, 2011 at 7:53
  • 5
    @agf Because list comprehensions are for creating lists, not just calling a function on each element of a sequence. Commented Apr 24, 2019 at 16:58

5 Answers 5

67

Either with lambda:

for bar in map(lambda foo: foo.get_bar(), foos):

Or simply with instance method reference on your instance's class:

for bar in map(Foo.get_bar, foos):

As this was added from a comment, I would like to note that this requires the items of foos to be instances of Foo (i.e. all(isinstance(foo, Foo) for foo in foos) must be true) and not only as the other options do instances of classes with a get_bar method. This alone might be reason enough to not include it here.

Or with methodcaller:

import operator
get_bar = operator.methodcaller('get_bar')
for bar in map(get_bar, foos):

Or with a generator expression:

for bar in (foo.get_bar() for foo in foos):
Sign up to request clarification or add additional context in comments.

7 Comments

In reverse order of how Pythonic they are.
Also, map(Foo.get_bar, foos) where Foo is the class of the objects in foos.
I decided to go with the lambda since I realised that I needed to do this transformation a couple places in the code, and Bars needed to be stringified and lowercased. :)
@Dan D. : What if an argument needs to be passed into get_bar() such as get_bar(val) ?
@Spade I know this is late, but maybe it will help others. Two ways to do this: 1) use a the variable val defined in a higher level scope (within the method defining the lambda or globally). Typically this would act as a constant per comprehension. Alternatively if you have a known sequence of parameters you can zip the objects and parameters together and use a lambda to pick out the object and parameter like this -- lambda xy: xy[0].get_bar(xy[1]) the foos would instead be xys = zip(foos,parameters). This can get ugly and a helper function or alternate design may be better.
|
12

You want operator.methodcaller(). Or, of course, a list or generator comprehension.

3 Comments

I guess list comprehensions are preferred in newer versions of Python, right? I'll be happy to accept your answer if you update it to include sample code using an LC for my example. :)
@JoshGlover "newer versions of Python"? They go back to at least 2.3.
List comprehensions were added in Python 2.0.
6

I think the cleanest way is to forgo the for-loop completely and use a list comprehension:

bars = [foo.get_bar() for foo in foos]

This answer is close to some other answers but differs in that the list is actually returned and not used in the subsequent loop. Depending on what you do with bars, you may be able to use a list comprehension there to.

I don't think the map function with lambdas are particulary readable, besides the overhead for map is considerable.

Comments

2

This modified code will work:

for bar in map(lambda f: f.get_bar(), foos):
# Do something with bar

You provide lambda function here. Simply providing get_bar doesn't work because it is accessible only through an instance of class (f.get_bar()), never by itself.

Comments

1

You can use lambda

for bar in map(lambda foo: foo.get_bar(), foos):
    # Do something with bar

Comments

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.