views:

92

answers:

2

I wrote a line of code using lambda to close a list of file objects in python2.6:

map(lambda f: f.close(), files)

It works, but doesn't in python3.1. Why?

Here is my test code:

import sys

files = [sys.stdin, sys.stderr]

for f in files: print(f.closed)   # False in 2.6 & 3.1

map(lambda o : o.close(), files)

for f in files: print(f.closed)   # True in 2.6 but False in 3.1

for f in files: f.close()        

for f in files: print(f.closed)   # True in 2.6 & 3.1
+7  A: 

map returns a list in Python 2, but an iterator in Python 3. So the files will be closed only if you iterate over the result.

Never apply map or similar "functional" functions to functions with side effects. Python is not a functional language, and will never be. Use a for loop:

for o in files:
    o.close()
Philipp
Note also that 2to3 catches this, and will automatically list(...) the map call for you, forcing immediate evaluation.
Joe
'Never apply map or similar "functional" functions to functions with side effects. Python is not a functional language, and will never be.' I don't see why this wouldn't be sound advice even if python were a functional language. There's simply no point in using map if you're not going to use the result - in any language.
sepp2k
In purely functional languages functions have no side effects, so a `close()` function cannot exist.
Philipp
@Philipp: Purely functional languages are a minority. In ML, you can have side effects as you like... but any ML programmer worth his salt will be avoid them nonetheless. It's not about language enforecements, it's about programming style.
delnan
@Philipp: Your phrasing made it sound as if you were saying "In functional languages it's ok to use map to close each file handle in a list, but not in python because python is not a functional language."
sepp2k
+4  A: 

Because map in Python 3 is a lazy iterator. Quoting the docs:

Return an iterator that applies function to every item of iterable, yielding the results.

E.g. in Python 2, map(f, seq) equivalent to [f(i) for i in seq], but in Python 3, it's (f(i) for i in seq) - subtly different syntax, but very different semantics. To make the map variant work, you'd need to consume the iterator. Ergo, it's simpler (and more idiomatic: map, comprehension and generators shouldn't have side effects!) to use an explicit for-loop.

delnan
In my opinion they should have left `map()` alone and made the itertools module `imap()` method a built-in. Instead all 'they' did was introduce a more major incompatibility by change what an existing built-in does.
martineau
Also, leaving `map()` alone and making `imap()` a built-in would have followed the "Explicit is better than implicit" philosophy better.
martineau
map itself is discouraged by 'them'. haha nice try. anyways, fuck 'em.
OTZ