Q1次のコードを実行したときの出力はどれですか?nums = [1, 2, 3]
print(list(map(lambda x: x + 10, nums)))
map() — リストの全要素に関数をまとめて適用する
Python の map() 関数を基礎から解説します。基本構文から lambda との組み合わせ、内包表記との使い分けまで図解で押さえます。
前回のデコレータでは、関数に処理を被せる書き方を見ました。今回はもう一度視点を変えて、「リストの全要素に同じ関数をまとめて適用したい」ときの組み込み関数 map() を整理します。
map() はラムダ式と組み合わせて使い、しくみ自体は「内包表記」と似ています。最近は map() よりも内包表記を推奨されることが多いです。
map() とは — 全要素に関数を適用する高階関数
map(関数, イテラブル) は、第 2 引数のリストやタプルなどの全要素に第 1 引数の関数を適用して、結果を 1 つずつ取り出せるmap オブジェクトを返します。for で 1 つずつ取り出すか、list() でまとめてリストに変換して使います。
第 1 引数に「関数」を取る点で、map() 自身も高階関数の仲間です。
map オブジェクトで返します。list() でリスト化して中身を取り出せます。numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x ** 2, numbers)
print(type(squared)) # <class 'map'>
print(squared) # <map object at 0x...>
# list() で中身を取り出す
print(list(squared)) # [1, 4, 9, 16, 25]
# 組み込み関数も渡せる(文字列のリストを int に変換)
str_nums = ["10", "20", "30"]
print(list(map(int, str_nums))) # [10, 20, 30]
map オブジェクトは「使い切り」
map() の戻り値はジェネレーターと同じイテレータで、1 度走らせると中身が空になります。list(squared) を 2 回呼ぶと、2 回目は [] になるので注意します。複数回使いたいときは list(map(...)) で先にリストにしてから扱いましょう。
複数のイテラブルを同時に渡す
map() は第 2 引数以降に複数のイテラブルを渡せます。その場合、第 1 引数の関数は同じ位置の要素を 1 つずつ受け取る必要があります。map(関数, A, B) と書けば、A[0] と B[0]、A[1] と B[1]、... のペアに関数が適用されます。
例えば 2 つの数値リストを掛け算したいとき、map(lambda a, b: a * b, A, B) と書けば、要素ごとの積のリストが取り出せます。
A = [1, 2, 3]
B = [10, 20, 30]
# 同じ位置の要素同士を掛け算
print(list(map(lambda a, b: a * b, A, B))) # [10, 40, 90]
# 3 つでも同様
def calculate(x, y, op):
return x + y if op == "plus" else x - y
xs = [10, 20, 30]
ys = [3, 3, 3]
ops = ["plus", "minus", "plus"]
print(list(map(calculate, xs, ys, ops))) # [13, 17, 33]
A と B の同じインデックスの要素がペアとなり、ラムダ式 lambda a, b: a * b に渡されます。長さが違うときは短いほうに合わせる
渡したイテラブルの長さがバラバラだと、map() は一番短いものに合わせてループを止めます。例えば map(f, [1, 2, 3, 4], [10, 20]) は 2 回しか実行されません。明示的に揃えたいときは、事前に長さチェックするか、用途に応じて `zip()` と組み合わせます。
map() vs リスト内包表記 — どちらを使うべきか
map() でできることは、ほぼすべてリスト内包表記でも書けます。Python 公式ドキュメントや書籍 Fluent Python でも「新規コードでは内包表記を優先する」と紹介されており、現在の Python では内包表記が標準的な書き方です。
map() は古いコードでは標準でしたが今は、関数名が決まっていてラムダを書くまでもないとき(map(int, str_nums) のような形)にすっきり書ける程度に役割が縮小しました。書き方の違いを並べて整理しておきましょう。
| やりたいこと | map() で書く | 内包表記で書く |
|---|---|---|
| 全要素を 2 乗 | list(map(lambda x: x ** 2, nums)) | [x ** 2 for x in nums] |
| str を int に変換 | list(map(int, str_nums)) | [int(s) for s in str_nums] |
| 2 つのリストを同時に | list(map(f, A, B)) | [f(a, b) for a, b in zip(A, B)] |
nums = [1, 2, 3, 4, 5]
# map() 版
print(list(map(lambda x: x ** 2, nums))) # [1, 4, 9, 16, 25]
# 内包表記版(こちらが推奨)
print([x ** 2 for x in nums]) # [1, 4, 9, 16, 25]
# 関数名を直接渡せるときは map() がすっきり
str_nums = ["10", "20", "30"]
print(list(map(int, str_nums))) # [10, 20, 30]
print([int(s) for s in str_nums]) # [10, 20, 30]
# 条件で絞りたいときは内包表記が圧倒的に簡潔
print([x for x in nums if x % 2 == 0]) # [2, 4]
迷ったら内包表記を選ぶ
「変換したい」「絞りたい」「両方やりたい」のいずれも、内包表記なら同じ括弧の中に自然に書けます。map() は 既存の関数名をそのまま渡したいときだけ使うと割り切ると、コードの一貫性が保ちやすくなります。読み書き両方の負担が減るので、新しく書くコードでは内包表記を優先してください。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2次のうち、list(map(int, ["1", "2", "3"])) と同じ結果になる内包表記はどれですか?
Q3map() の戻り値について正しいものはどれですか?