静かなる名辞

pythonとプログラミングのこと


【python】mapで複数の引数を渡したいときはstarmapが便利

 pythonにはmapという関数があります。関数型プログラミングに役立ち、大変便利な関数です。

 しかし、これはデフォルトでは一つの引数を前提としています。

>>> list(map(lambda a,b: a+b, zip([1,2,3],[4,5,6])))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'b'

 困ったものですね。こういうときは関数の側で工夫してあげるしかないのでしょうか?

>>> list(map(lambda x: x[0]+x[1], zip([1,2,3],[4,5,6])))
[5, 7, 9]

 内包表記使えよ、という考え方もあると思いますが、不問とします。

 こういうケースでうまく働くのが、itertools.starmapです。
10.1. itertools — 効率的なループ実行のためのイテレータ生成関数 — Python 3.6.5 ドキュメント

 starはunpackingのstar。こう書くともうだいたい察してしまった人もいるでしょうが、こう書けます。

>>> from itertools import starmap
>>> list(starmap(lambda a,b: a+b, zip([1,2,3],[4,5,6])))
[5, 7, 9]

 引数が格納されたtupleは、lambdaに渡されるときにunpackされる訳です。気が利いてますね。

 これと同様にデフォルト引数を渡せるitertools.starstarmapもあります・・・と言いたいところですが、ありませんでした。残念。

それでも内包表記で書きたい人へ

 まあ、これのためにimportするくらいなら・・・という気持ちはとてもよくわかりますし、私も普段はそうするクチなんですが。

>>> [a+b for a,b in zip([1,2,3],[4,5,6])]
[5, 7, 9]

 listとlambdaが端折れる分だけ短く書けているように見えますが、返り値がジェネレータで構わなくて、既存の関数を呼び出したいときはmapの方が短くなる(こともある)ようです。

追記

 この記事書いたときはすっかり忘れていたけど、そういえばmapはデフォルトで複数の引数を取れる。

>>> list(map(lambda a,b:a+b, [1,2,3],[4,5,6]))
[5, 7, 9]

 普通にこっちで良いですね。

 zipされたものを入力に渡したいときは工夫が必要だが、unpackしてもう一回zipしてまたunpackすれば良い。

>>> list(map(lambda a,b:a+b, *zip(*zip([1,2,3],[4,5,6]))))
[5, 7, 9]