関数

【Python】map関数の使い方の基本 ~リスト要素への関数適用~

【Python】map関数の使い方の基本 ~リスト要素への関数適用~

Pythonの組み込み関数であるmap関数を使ってリスト要素へ関数を適用する方法を解説します。

map関数でリストの要素へ関数を適用する

map関数は、関数と処理対象のイテラブルを受け取って、イテラブルの各要素に関数を適用した結果を返すPythonの組み込み関数です。

このように引数に関数を受け取る関数は高階関数と言います。for文で処理しても同じ処理ができますが、繰り返し処理を書かずともシンプルに記載できる点がメリットです。

map関数は、関数型プログラミング言語において中心的な関数です。Pythonはマルチパラダイムの言語と言われているため、厳密ではありませんが関数型のプログラミングスタイルもサポートしており、map関数が組み込み関数として利用できるようになっています。

本記事では、リストの要素へ関数を適用する例などを用いて、map関数の使い方の基本を紹介します。

Note

map関数と同じく関数型プログラミングに関連のある関数としてfilter関数、reduce関数、partial関数があります。以下のページでまとめていますので興味があれば参考にしてください。

map関数の基本的な使い方

map関数の基本的な構文は以下のようになります。

map関数
result = map(関数, イテラブル)

複数引数をとる関数を指定する場合には、以下のようにイテラブルを列挙して指定することができます。

result = map(関数, イテラブル1, イテラブル2, ...)

map関数では、イテラブルの各要素に関数を適用した結果のイテレータを返却します。イテレータは値を逐次取り出せるオブジェクトで、nextを用いることで順に関数を適用した値を取り出したり、for文で使用することができます。後述の例でも見ますが、list(map(関数, イテラブル))のようにすることでリストに変換して取得することも可能です。

また、map(関数, イテラブル1, イテラブル2, ...)というように複数のイテラブルを渡す場合には関数も引数としてイテラブルの数だけ受け取れる必要があります。

なお、イテレータについては「イテレータ(iterator)の基本」でまとめていますので興味があれば参考にしてください。

以降で、map関数のいくつかの使い方について例を使ってみていきましょう。

基本的な使い方

map関数の基本的な使い方を以下の例を用いて見ていきましょう。

def square(x):
    """値を2乗する"""
    return x**2


if __name__ == "__main__":
    tmp_list = [1, 2, 3, 4, 5]

    # map関数を適用し、イテレータを取得
    result = map(square, tmp_list)
    print(result, "\n")

    # nextで1要素ずつ結果データを取得
    print(next(result))
    print(next(result))
    print(next(result))
    print(next(result))
    print(next(result))
【実行結果】
<map object at 0x0000018E82BC2A70> 

1
4
9
16
25

上記の例では、受け取った値を2乗するsquareという関数が定義されており、tmp_listの各要素に適用しています。

map関数を呼び出している部分では「map(square, tmp_list)」というように関数と処理対象のリストを渡しています。ここでポイントは、関数を渡す際に()がないことです。関数はsquare(2)のように()で引数を指定すると関数を実行することを意味しますが、()がない場合は関数そのものを表します。map関数は、受け取ったsquareという関数を内部で実行しているわけです。

map関数の返却値は「map object」となっていますが、これはイテレータの一種です。next関数を使用することで次の要素を順番に取得することができます。この例では、tmp_listの要素それぞれを2乗した結果が順に取得されていることが分かります。

上記例でnext(result)を追加して再度実行するとStopIteration例外が発生します。これはイテレータがもう要素を持たない場合に発生する例外です。StopIterationは、forループ等の繰り返しの終了を示すために使用されます。

【実行結果】
Traceback (most recent call last):
...(省略)...
StopIteration

リストやタプルで取得する場合

map関数の返却値はイテレータであるため、以下のようにlisttupleに変換することで、リストやタプルとして結果を取得することができます。

def square(x):
    """値を2乗する"""
    return x**2


if __name__ == "__main__":
    tmp_list = [1, 2, 3, 4, 5]

    # map関数を適用し、リストで取得
    result_list = list(map(square, tmp_list))
    print(result_list)

    # map関数を適用し、タプルで取得
    result_tuple = tuple(map(square, tmp_list))
    print(result_tuple)
【実行結果】
[1, 4, 9, 16, 25]
(1, 4, 9, 16, 25)

上記結果を見るとリストやタプルとして結果を受け取れていることが分かるかと思います。

複数イテラブルを指定する場合

map関数は複数のイテラブルを受け取り、それらに対してまとめて関数を適用することができます。以下の例では、make_variable_setという関数が3つの引数を受け取り、それらをタプルとして返す動作をします。

def make_variable_set(x, y, z):
    """各引数を並べたタプルを返却する"""
    return x, y, z


if __name__ == "__main__":
    nums1 = [1, 2, 3]
    nums2 = [4, 5, 6]
    nums3 = [7, 8, 9]

    # map関数で複数のイテラブルを渡す
    # 関数側もイテラブルの数だけの引数を受け取れる必要がある
    result = list(map(make_variable_set, nums1, nums2, nums3))
    print(result)
【実行結果】
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

この結果を見ると、3つのリストの各要素が関数によってタプルとしてまとめられて返されていることが確認できます。

map(関数, イテラブル1, イテラブル2, ...)というように複数のイテラブルを渡す場合、関数がそれらのイテラブルの各要素数に対応する引数を受け取るように設計されている必要があります。

複数のイテラブルの要素数が異なる場合

map関数を使用する際、複数のイテラブルを指定することができますが、それらの要素数が異なる場合の挙動に注意が必要です。

具体的には、map関数は、最も短いイテラブルの要素数に合わせて処理を行います。これは最も短いイテラブルが終了した時点で、他のイテラブルの残りの要素は無視されることを意味します。

def make_variable_set(x, y, z):
    """各引数を並べたタプルを返却する"""
    return x, y, z


if __name__ == "__main__":
    nums1 = [1, 2, 3]
    nums2 = [4, 5, 6, 7, 8, 9]
    nums3 = [10, 11, 12, 13, 14]

    # map関数で複数のイテラブルを渡す
    # 最も短いイテラブルが消費されたら終了
    result = list(map(make_variable_set, nums1, nums2, nums3))
    print(result)
【実行結果】
[(1, 4, 10), (2, 5, 11), (3, 6, 12)]

この例では、3つの要素を使用していますが、最も短いイテラブルであるnums1に合わせて処理が行われるため、nums2の7, 8, 9やnums3の13, 14については結果に含まれていないことが分かります。

ラムダ関数(無名関数)と組み合わせる場合

map関数は、ラムダ関数(無名関数)と組み合わせることで、一時的な小さな関数を定義せずに使用することができます。

ラムダ関数は関数型プログラミングでもとても重要な概念です。ラムダ関数については「ラムダ(lambda)関数:無名関数の使い方」でまとめていますので興味があれば参考にしてください。

上記で見てきたsquare関数をラムダ関数にしてmap関数で使う例を以下で見てみましょう。

tmp_list = [1, 2, 3, 4, 5]

# map関数を適用し、リストで取得
result = list(map(lambda x: x**2, tmp_list))
print(result)

# map関数を適用し、タプルで取得
result = tuple(map(lambda x: x**2, tmp_list))
print(result)
【実行結果】
[1, 4, 9, 16, 25]
(1, 4, 9, 16, 25)

これまでの例でsquareと関数を指定していた部分に「lambda x: x**2」というようなラムダ関数を指定しています。結果はsquare関数を使った場合と同じであることが分かります。

ラムダ関数は無名関数ともいわれる通り、関数名はありませんが式そのものが関数扱いになります。そのため、map関数にラムダ関数を渡すことでリストの各要素に関数を適用できます。

まとめ

Pythonの組み込み関数であるmap関数を使ってリスト要素へ関数を適用する方法を解説しました。

map関数は、関数と処理対象のイテラブルを受け取って、イテラブルの各要素に関数を適用した結果を返すPythonの組み込み関数です。map関数は他の関数型プログラミング言語でも中心的な関数でPythonでも使用できます。

map関数はfor文を用いなくてもシンプルに関数の適用ができることが特徴です。非常に汎用的で強力なものですが、map関数の適用が常に最適な選択というわけではありません。場合によってはfor文で書いた方が可読性が上がる可能性もあり、また、大規模なデータ処理ではNumPyを使用する方がよいケースも考えられます。

目的に応じて適切に方法を選択できるように、map関数の使い方を理解し、覚えておいてもらうとよいかと思います。