map() and list comprehensions
March 28, 2005 |
co.mments
It looks likely that the lisp-style functions in Python such as map() will be removed, (along with lambda). The arguments for doing this seem to come down to two things: that you can use list comprehensions to provide the same functionality, and, that people don't like Lispisms (ie introducing them into Python way back when was a mistake that needs to be rectified). Various incidental arguments around speed also come up, but they don't matter.
I never fully understood the excitement around list comps when they were introduced. I suspect that because you can see just enough of the innards, a list comp is impressive, a bit like having a glass hood over a car engine or a perspex kettle. On the other hand , map() is opaque, even dull.
I imagine I like the map() approach because functions tend to nest and compose more elegantly than extra syntax. Removing top-level functions like map() from Python in favor of list comps fees like a step backwards. I don't see is how this:
mods = [__import__(mod) for mod in testmodules]
is ever going to be more elegant than this:
mods = map(__import__, testmodules)
March 28, 2005 10:43 PM
Comments
pgs 437-438 of Text Processing In Python cover this pretty well. List comps are probably better if you're just looking to eliminate nested loops. If you're actually composing functions, I'm not so sure.
(runs upstairs to get the Mertz book...) Indeed it does cover it. Thanks for the pointer.
For loops do take up a lot of space. I remember a colleague and I looking at a codebase once and guesstimating that ~25% of the code would disappear if map() or list comps were used.
I suppose nesting functions is Lisponic, but not Pythonic. Or something like that.
I'm not familiar with comprehensions in Python. If you look at Erlang, Haskell, etc. then comprehensions can be as expressive as a query language using joins, etc. i.e. more than one list can be in the comprehension, comparing elements, and projecting values based on elements, from each list.
This has definitely not been my experience. For instance, [s.lower() for s in lst] is way better than map(lambda s: s.lower(), lst) -- the Lispy functions just don't work well with methods and OO conventions.
I don't really see the composition as any better either. Is this: map(lambda s: s.lower(), map(str, lst)) better than [str(s).lower() for s in lst] ?
I just wonder why Python can't keep both syntax. Anyway, it is just syntax. However I like map(func,list) since explicitly expressing a loop over the list makes it a bit verbose.
Ian,
that's kind of a setup. If you've got functions assigned to free variables, it works better:
map(string.lower, map(str, lst))
It's the "if" part of listcomps that make it more powerful than map. I suppose map() becomes a lambda for a listcomp now? :)
For people that refactor their initial for-loops into a reduced form like map or a listcomp, I think the listcomp wins because it's easier to see how to refactor into that form as opposed to map (most of the time). See this post for an example.
Of course, if you come from a Lisp background, this statement probably isn't true.
I just had to think about this because of pylint...
', '.join([a[0] for a in d])
is really nicer and easier to understand than
', '.join(map(lambda a:(a[0]),d))