Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I always struggle to understand why a list comprehension

  alist = [foo(word) for word in words]
is considered more Pythonic than map

  alist = map(foo, words)


List comprehensions are more flexible and easier to read in the non-trivial case. Sure in the trivial case you show a map might be considered neater, but just adding a filter is enough to make the list comprehension more readable in my mind. Python's lambda syntax also makes using maps and filters quite ugly.

Compare:

   alist = [x**2 for x in mylist if x%3==0]
to

   alist = map(lambda x: x**2,filter(lambda x: x%3==0, mylist)

Plus python also has set comprehension and dict comprehension, which share essentially the same syntax.


Don't forget generator comprehensions which are almost identical to list comprehensions. But instead of evaluating the whole result set and returning it, they return a generator that you can then iterate over. Very neat stuff.


In Haskell, the latter looks like:

  alist = (map (**2) . filter (\x -> x `mod` 3 == 0)) myList
Or:

  alist = (map (**2) . filter ((== 0) . (`mod` 3))) myList
If alist is a transformation, and not applied to myList, it's cleaner:

  alist = map (**2) . filter ((== 0) . (`mod` 3))
Though Haskell also has list comprehensions, with more "mathy" syntax:

  alist = [x**2 | x <- mylist, x `mod` 3 == 0]


hi, you are lacking a closing parenthesis in your second example.


    alist = [foo(word) for word in words if word.startswith('a')]
    alist = map(foo, filter(lambda word: word.startswith('a'), words))
Which reads better?


I don't use FP practices in Python much, but if I did I'd define the filter outside the map, like so:

    begins_with_a = lambda x: x.startswith('a')
    alist = map(foo, filter(begins_with_a, words))


Even with named functions, Python's use of global functions instead of methods for iterators force you to read the expression from the inside out. I think Lisp languages nailed this with their threading macros, which allow natural left-to-right reading, but Ruby's strategy is better than Python's, too, while maintaining very similar syntax.

    ;; clojure
    (let [begins-with-a #(.startsWith % "a")
          foo #(do-some-stuff-with %)]
      (-> words (filter begins-with-a) (map foo))))

    # ruby
    words.filter { |e| e.start_with?(?a) }.map { |e| foo(e) }
It doesn't really make sense for things like `len` and `map` to be global functions in object-oriented languages.


Those are not equivalent. You need to wrap the map in a list() call.


In Python 3 you'd be right, but I think people still call mostly that language "Python 3" and mean Python 2 when they say "Python".


Admittedly, my thought would be to chain the calls, not nest them:

   alist = words.filter(lambda word: word.startswith('a')
                .map(foo)
That being said, my Python is limited and I don't know it filter/map are available as methods of a list. At the end of the day, there are cases where list comprehensions are much cleaner/understandable... and cases where the reverse is true.


> I don't know it filter/map are available as methods of a list.

They're not. Which is a shame in my opinion, because as you've written it you can clearly read the operations in the order they happen, ie. filter followed by map. Instead, you do have to do the second line of what blossoms wrote above.

And I don't think it's possible to write a list comprehension that reads in execution order, either :(


Map and filter, of course; less syntactical noise, simple function semantics, and plenty of precedent and equivalents in all other languages.


It's not like list comprehensions lack equivalent in other languages. Let's take Haskell for example. Python lists comprehension could use a where clause from Haskell though so one could really pack everything into a one-liner :)

(as it is now, list comprehensions requiring various references to result of a function call evaluate the function each time it's used)


you can consider a list comprehension to be a sort of literal representation of the result of map. I think literals have a benefit for code readability and should be used when feasible (i.e. the literal is compact enough).

the other reason its considered more idiomatic in Python is just because the compiler does a better job of parsing and optimizing list comprehensions.


You use list comprehensions in other places you wouldn't use map. The difference in text size as you posted is minimal, but the list comprehension - once you're used to reading them - tells you exactly what's going on.

Where as map could be anything. It could be redefined for all you'd know.


Don't know if this is why, but the list comprehension takes an expression at the "foo(word)" location, and is therefore more general than map, which requires a function. The comprehension in that case is simpler.

  words = ['w1', 'w2', 'w3']

  [word[1] for word in words]

  ['1', '2', '3']
  
  map(lambda x: x[1], words)

  ['1', '2', '3']
I like looking at the list comprehension better. The use of lambda looks forced in this case. I also imagine there's a penalty for calling the (anonymous) function in map.


In that case I'd use

  map(itemgetter(1), words)


Instead of my map example, or instead of a list comprehension in general?


Instead of a list comprehension equivalent to your example.


Those are not equivalent.

You cannot index a map object, for example.


I think the main reason is the lambda syntax. List comprehensions also let you do filter and nested loops.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: