filter, map に指定する関数

この記事は Python Tips Advent Calendar 2012 10日目の記事です。


通常、filter, map の第一引数には関数(callable なオブジェクト)を指定しますが、これは None であっても構いません。
None を指定した場合、lambda x: x と大体同じ意味になります*1

>>> filter(None, [0,1,[],3,None])
[1, 3]
>>> map(None, [1,2])
[1, 2]

暗黙bool値変換 で書いたように、x が 0 や [] や None だった場合は False として扱われるので、そういった要素を除けるときに使えます。


map は、この例だと全く意味が無いように見えるかもしれませんが、例えば以下のように複数のリストを纏める場合に使えます。

>>> map(lambda a,b: (a,b), [1,2,3], [4,5,6])
[(1, 4), (2, 5), (3, 6)]
>>> map(None, [1,2,3], [4,5,6]) # 短く書ける
[(1, 4), (2, 5), (3, 6)]

これは zip とよく似た動作になりますが、zip は短いリストに、map は長いリストに合わせられるという点が異なります。

>>> zip([1,2], [1,2,3,4])
[(1, 1), (2, 2)]
>>> map(None, [1,2], [1,2,3,4])
[(1, 1), (2, 2), (None, 3), (None, 4)]

None になるのが嫌な場合、以下のように書けばデフォルト値を入れることができます。

>>> map(lambda a,b: (a if a is not None else 0,
...                  b if b is not None else 0),
...                 [1,2], [1,2,3,4])
[(1, 1), (2, 2), (0, 3), (0, 4)]

ただし、リストの中に None が含まれていた場合はそれもデフォルト値になってしまいます。

>>> map(lambda a,b: (a if a is not None else 0,
...                  b if b is not None else 0),
...                 [1,2], [1,None,3,4])
[(1, 1), (2, 0), (0, 3), (0, 4)]

こういう場合は itertools.izip_longest を使うのがいいでしょう*2

>>> from itertools import izip_longest
>>> list(izip_longest([1,2], [1,None,3,4], fillvalue=0))
[(1, 1), (2, None), (0, 3), (0, 4)]


.

*1:大体、と付けたのは map の場合は lambda *args: args になるから

*2:Python3 なら zip_longest という名前らしい