[Tutor] map, filter and lambda functions

Allan Crooks allan.crooks@btinternet.com
Thu, 23 Aug 2001 05:55:55 +0100


On 22 Aug 2001, at 20:59, Sheila King wrote:

> :> checklist = string.split(filetext, "\n")
> :> checklist = filter(lambda x: x != '', checklist)
> :> checklist = map(lambda x: string.strip(x), checklist)
> :> checklist = map(lambda x: string.upper(x), checklist)
> 
> :My opinion is, the best version is the one which makes you smile
> :more. :) Out of the two, I like the second one more.
> 
> Well, I really was asking for an outside opinion, and someone who knew
> a bit more about optimizing the code.

The way I see it, the code is fairly optimal. If you want it more 
optimal, you'd probably have to write it in C. :)

> Are you sure that you can do this without lambda? I tried today, and
> couldn't get it to work without lambda.
> 
> >>> mylist
> ['a', 'b', 'c', '']
> >>> mylist = map(string.upper(x), mylist)
> Traceback (most recent call last):
>   File "<pyshell#6>", line 1, in ?
>     mylist = map(string.upper(x), mylist)
> NameError: name 'x' is not defined
> >>> mylist = map(string.upper(), mylist)
> Traceback (most recent call last):
>   File "<pyshell#7>", line 1, in ?
>     mylist = map(string.upper(), mylist)
> TypeError: upper() takes exactly 1 argument (0 given)
> >>> 

Let me show you this little code fragment:

>>> mylist = ['a', 'b', 'c', '']
>>> import string
>>> map (string.upper, mylist)
['A', 'B', 'C', '']
>>>

As you can see, it does work.

map takes a function and a sequence. It then takes each element 
in the sequence and then attempts to call that function with the 
element as an argument.

string.upper() doesn't work, because you're trying to run the 
function with no arguments (as it says). string.upper(x) doesn't 
work either, because x isn't defined anywhere (as it says again).

string.upper works, because map needs to be passed a function 
which it can use.

So, general rule is, if you have a function f:
  a = f # a holds the function f
  b = f(..someargs...) # b holds the result of running f with some 
args

It's a common mistake that is made, people mean to pass a 
function around, but accidently end up executing the function itself.

> :checklist = map (lambda x: string.upper(string.strip(x)), checklist)
> : :Which reduces it down to one line. But you might not want that.
> 
> Not too readable, IMO.

Agreed. But it's a great way of doing things if we were programming 
in a functional language. :)

> Hmm. That seems to work:
> 
> >>> mylist
> ['a', 'b', 'c', '']
> >>> filter(None, mylist)
> ['a', 'b', 'c']
> >>> 
> 
> Now I'm curious as to *why* it works. 'None' doesn't strike me as a
> function that returns a value. Although, it says in the documentation
> somewhere, that None on map works as the identity function. This
> didn't bother me, though. It seemed like "No map...gives you same
> thing you started with."

*Why* it works? Just because filter is designed to work that way. 
Initially I was going to suggest a way of doing that, but instead of 
None, I was going to suggest the truth function from the operator 
module.

None certainly isn't a function though.

> 
> :If you were really daring, you could put all that into one large
> :function: : :checklist = map (string.upper, map (string.strip, filter
> (None, :string.split(filetext, "\n")))) : :That *might* work, I can't
> be bothered to test it. :)
> 
> OK, two remarks on this:
> First of all, does putting it all in one line make it run faster or
> more efficiently? My intuition says, "No".

It might make it ever so slightly faster, but by "slightly faster", I 
mean that *technically* it might be faster, but you wouldn't notice it.

You only make one assignment to checklist, instead of four. 
Technically, there's the overhead of assigning a value to a name, 
but it's far from noticable.

> Secondly, it's definitely harder to follow. I would be very
> disinclined to writing it like that. Having the code be compressed
> into a single line really isn't that appealing.

I completely agree with you there. Just because you can do 
something, doesn't mean you should. :)

Besides, considering Python code is meant to be readable, it 
would be a bad idea anyway. But I was showing you another way 
of putting it, "just because I could". :)

> :Use whatever version you are happiest with. I'd go with whatever
> :version is easier to understand when you read it, which, out of the
> :two you've given us, would be the second one.
> 
> That's an interesting remark. Until today, I wouldn't have found those
> filter and map, or lambda functions easy to read and understand.
> Something clicked today, and now I'm fine with them. But before, I
> would have preferred the nested loop stuff I had originally.

It's just something that comes with practice I suppose. The nested 
loop version was a lot harder to read, but the second version is a 
lot easier to understand, and a lot shorter too. I know that it's also 
fairly satisfying when it just "clicks" into place. :)

Allan.