- Python
Python map・filterの使い方|lambdaと内包表記の違い早見表
map は「すべての要素に同じ関数を当てて変換する」、filter は「条件に合う要素だけを残す」ための組み込み関数です。 どちらも結果をリストではなくイテレータ(1個ずつ順に取り出す仕組み)で返すので、中身を見たいときは list() で包みます。同じ処理は内包表記やジェネレータ式でも書けるため、「どれで書けばよいのか」で迷う人が多いテーマです。
この記事では、map・filter・内包表記・ジェネレータ式を同じ処理で横に並べた対応早見表を最初に置き、すべてのコードに Python 3.13.7 と 3.14.7 での実行結果を付けました。<map object at ...> と表示される、2回目に回すと空になる、といったつまずきをそのまま再現し、timeit で測った100万件の処理時間も数値で載せています。複数リストを同時に扱う map、None を渡す filter、itertools への発展まで、1ページでたどれる構成です。
map・filter・内包表記・ジェネレータ式の対応早見表
nums = [1, 2, 3, 4, 5, 6] に対して同じ処理をしたときの書き方です。右の2列は、左の map / filter と同じ結果になります。
| やりたいこと | map / filter | リスト内包表記 | 結果 |
|---|---|---|---|
| 全要素を2倍にする(変換) | list(map(lambda x: x * 2, nums)) |
[x * 2 for x in nums] |
[2, 4, 6, 8, 10, 12] |
| 偶数だけ残す(抽出) | list(filter(lambda x: x % 2 == 0, nums)) |
[x for x in nums if x % 2 == 0] |
[2, 4, 6] |
| 偶数だけ2倍にする(抽出+変換) | list(map(lambda x: x * 2, filter(lambda x: x % 2 == 0, nums))) |
[x * 2 for x in nums if x % 2 == 0] |
[4, 8, 12] |
| 文字列に変換する | list(map(str, nums)) |
[str(x) for x in nums] |
['1', '2', ...] |
| 空や 0 を取り除く | list(filter(None, vals)) |
[v for v in vals if v] |
真と判定される値だけ |
| 2つのリストを足し合わせる | list(map(lambda a, b: a + b, xs, ys)) |
[a + b for a, b in zip(xs, ys)] |
要素ごとの和 |
4つの書き方の性質の違いは次のとおりです。
| 書き方 | 返すもの | 評価のタイミング | 繰り返し使えるか | 向いている場面 |
|---|---|---|---|---|
map(f, it) |
map オブジェクト(イテレータ) | 取り出すときに1個ずつ | 1回だけ | 既存の関数をそのまま当てる |
filter(f, it) |
filter オブジェクト(イテレータ) | 取り出すときに1個ずつ | 1回だけ | 既存の判定関数で絞る、None で空を除く |
[式 for x in it if 条件] |
リスト | 書いた時点で全部 | 何度でも | 式や条件をその場で書く。結果を何度も使う |
(式 for x in it if 条件) |
ジェネレータ(イテレータ) | 取り出すときに1個ずつ | 1回だけ | sum や any にそのまま渡す。大量データ |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python map・filterの使い方|lambdaと内包表記の違い早見表」
検証した環境
| 項目 | 内容 |
|---|---|
| Python | 3.13.7 と 3.14.7(どちらも CPython) |
| OS | Linux(2 vCPU のコンテナ) |
| 外部ライブラリ | なし(標準ライブラリだけ) |
| 出力の違い | map(..., strict=True) とオブジェクトのサイズ以外は、3.13 と 3.14 で同じ出力でした |
「実行結果」は Python 3.14.7 の出力です。3.13 で違う箇所はその場で補足しています。
mapの使い方:全要素を変換する
map(関数, イテラブル) は、イテラブルの各要素に関数を当てた結果を順に返します。 第1引数は「関数そのもの」です。str() のようにかっこを付けず、str と名前だけ渡します。
nums = [1, 2, 3, 4, 5, 6]
print(list(map(lambda x: x * 2, nums))) # lambda を渡す
print(list(map(str, nums))) # 組み込み関数を渡す
print(list(map(int, ["1", "2", "3"]))) # 文字列を数値に
print(list(map(str.strip, [" a ", "b "]))) # メソッドを渡す
print(list(map(len, ["apple", "fig"]))) # 長さを求める
def tax(p): # 自作の関数を渡す
return round(p * 1.1)
print(list(map(tax, [100, 250])))
実行結果:
[2, 4, 6, 8, 10, 12]
['1', '2', '3', '4', '5', '6']
[1, 2, 3]
['a', 'b']
[5, 3]
[110, 275]
map に渡せる「関数」の形を表にまとめます。
| 渡すもの | 例 | 補足 |
|---|---|---|
| 組み込み関数 | map(str, nums)、map(int, strs)、map(len, words) |
もっとも短く書ける |
| 型のメソッド | map(str.upper, words)、map(str.strip, lines) |
型名.メソッド名 の形 |
| 自作関数 | map(tax, prices) |
def で定義した関数名 |
| lambda | map(lambda x: x * 2, nums) |
その場で1行の関数を作る |
| operator モジュール | map(itemgetter("name"), users) |
辞書のキーや属性の取り出しに便利 |
辞書のリストから特定のキーだけ取り出す例です。
from operator import itemgetter, methodcaller
users = [{"name": "sato", "age": 30}, {"name": "suzuki", "age": 17}]
print(list(map(itemgetter("name"), users)))
print(list(map(methodcaller("upper"), ["a", "b"])))
実行結果:
['sato', 'suzuki']
['A', 'B']
filterの使い方:条件に合う要素だけ残す
filter(関数, イテラブル) は、関数が真(True と判定される値)を返した要素だけを順に返します。
from itertools import filterfalse
nums = [1, 2, 3, 4, 5, 6]
print(list(filter(lambda x: x % 2 == 0, nums))) # 偶数だけ
print(list(filter(str.isdigit, ["12", "a3", "45"]))) # 数字だけの文字列
print(list(filterfalse(lambda x: x % 2 == 0, nums))) # 条件に合わないほうを残す
実行結果:
[2, 4, 6]
['12', '45']
[1, 3, 5]
itertools.filterfalse は filter の反対で、関数が偽を返した要素を残します。条件を not で書き直さずに済むので、既存の判定関数を使い回したいときに便利です。
None を渡すと、空や 0 をまとめて取り除ける
第1引数に None を渡すと、「その値自体が真かどうか」で判定します。 0、空文字列、None、空リスト、False が取り除かれます。
vals = [0, 1, "", None, "a", [], [0], False, True, 0.0, "0"]
print(list(filter(None, vals)))
print([v for v in vals if v]) # 同じ意味の内包表記
print(list(filter(lambda v: v is not None, [0, None, "", 1]))) # None だけ除く
実行結果:
[1, 'a', [0], True, '0']
[1, 'a', [0], True, '0']
[0, '', 1]
"0"(文字列のゼロ)と [0](要素が1個あるリスト)は空ではないので残ります。逆に数値の 0 や 0.0 は取り除かれます。0 を残したいときは filter(None, ...) ではなく、lambda v: v is not None のように条件を明示すると意図どおりになります。
Python 公式ドキュメント(組み込み関数のページ、3.14 版)にも、function が None のときは恒等関数とみなし、偽である要素がすべて取り除かれること、filter は (item for item in iterable if function(item)) というジェネレータ式と同等であることが書かれています。今回そのページを開いて確認しました。
lambdaの書き方:その場で作る1行の関数
lambda は「名前を付けずに、その場で1行の関数を作る」書き方です。 lambda 引数: 戻り値にする式 の形で、return は書きません。
sq = lambda x: x * x
print(sq(4), sq.__name__)
print((lambda x, y=2: x ** y)(3)) # デフォルト引数も使える
users = [{"name": "sato", "age": 30}, {"name": "suzuki", "age": 17}]
print(sorted(users, key=lambda u: u["age"])) # sorted の key に渡す
print(list(map(lambda x: "even" if x % 2 == 0 else "odd", [1, 2]))) # 条件式
実行結果:
16 <lambda>
9
[{'name': 'suzuki', 'age': 17}, {'name': 'sato', 'age': 30}]
['odd', 'even']
def との対応は次のとおりです。
| 項目 | lambda | def |
|---|---|---|
| 書ける量 | 式1つだけ | 複数行、if 文、for 文、try も書ける |
| 名前 | なし(__name__ は <lambda>) |
あり(エラー表示で関数名が出る) |
| 向いている場面 | map・filter・sorted の引数に1回だけ渡す | 何度も使う、処理が2行以上になる |
| 説明文(docstring) | 付けられない | 付けられる |
1回しか使わない短い処理は lambda、名前を付けて読みやすくしたい処理は def と使い分けると整理しやすくなります。
ループ内で作った lambda は、最後の値を見る
lambda の中で外側の変数を使うと、値ではなく変数そのものを参照します。ループで複数の lambda を作ると、全部が最後の値を返します。
fs = [lambda: i for i in range(3)]
print([f() for f in fs])
fs = [lambda i=i: i for i in range(3)] # デフォルト引数で値を固定
print([f() for f in fs])
実行結果:
[2, 2, 2]
[0, 1, 2]
i=i とデフォルト引数にすると、作った時点の値が固定されます。
つまずきやすい点1:map objectと表示される
map や filter の結果をそのまま print すると、中身ではなくオブジェクトの種類と場所が表示されます。
nums = [1, 2, 3, 4, 5, 6]
m = map(lambda x: x * 2, nums)
print(m)
print(filter(None, nums))
print(x for x in nums)
実行結果(0x 以降のアドレスは実行のたびに変わるため、末尾を省略しています):
<map object at 0x7f...>
<filter object at 0x7f...>
<generator object <genexpr> at 0x7f...>
これはエラーではなく、「まだ計算していない、取り出し待ちのイテレータです」という表示です。中身を見るには、用途に合った形に変換します。
| ほしい形 | 書き方 | 結果の例 |
|---|---|---|
| リスト | list(map(abs, [-1, 2])) |
[1, 2] |
| タプル | tuple(map(abs, [-1, 2])) |
(1, 2) |
| 集合 | set(map(abs, [-1, 1, 2])) |
{1, 2} |
| 文字列に連結 | ",".join(map(str, [1, 2, 3])) |
1,2,3 |
| 合計・最大 | sum(map(len, ["ab", "cde"]))、max(map(abs, [-5, 3])) |
5、5 |
| 複数の変数に分ける | a, b = map(int, "3 5".split()) |
a は 3、b は 5 |
| for で順に使う | for v in map(f, data): |
list() は不要 |
sum・max・join・for のように「順に受け取る側」に渡すなら、list() は付けなくて構いません。 list() が必要なのは、表示したい、長さを知りたい、添え字で取り出したい、何度も使いたい、というときです。
イテレータのまま len() や添え字を使うと、次のエラーになります。
m = map(lambda x: x * 2, [1, 2, 3])
len(m)
実行結果:
TypeError: object of type 'map' has no len()
m = map(lambda x: x * 2, [1, 2, 3])
m[0]
実行結果:
TypeError: 'map' object is not subscriptable
どちらも m = list(m) としてから使えば通ります。
つまずきやすい点2:2回目に回すと空になる
map・filter・ジェネレータ式は、1回最後まで取り出すと中身がなくなります。 これがリストとのいちばん大きな違いです。
m = map(str.upper, ["a", "b", "c"])
print(list(m))
print(list(m)) # 2回目
実行結果:
['A', 'B', 'C']
[]
エラーにならず、静かに空リストになる点が気づきにくいところです。同じ仕組みで、次のような動きも起こります。
m = map(str.upper, ["a", "b", "c"])
print("A" in m) # in で探すと、見つかった位置まで進む
print(list(m)) # 残りだけが出てくる
m = map(str.upper, ["a", "b", "c"])
print(max(m))
print(max(m)) # 2回目は空なのでエラー
実行結果:
True
['B', 'C']
C
ValueError: max() iterable argument is empty
また、イテレータは中身が空でも真と判定されます。
print(bool(map(str, [])), bool(list(map(str, []))))
実行結果:
True False
if filter(...): のように書くと、該当が0件でも常に真になります。「1件でもあるか」を調べたいときは any(...)、件数を知りたいときは list() にしてから len() を使います。
直し方をまとめます。
| 症状 | 原因 | 直し方 |
|---|---|---|
2回目の list() が [] になる |
1回目で最後まで取り出した | 最初に data = list(map(...)) として、data を使い回す |
in のあとに要素が減る |
探した位置まで進んだ | リストにしてから in を使う |
max() iterable argument is empty |
取り出し済みで空 | リストにしておく、または max(m, default=None) |
if m: が常に真になる |
イテレータは空でも真 | any(m) や len(list(m)) で判定 |
| next() で StopIteration | これ以上要素がない | next(m, 既定値) と第2引数を渡す |
next() の例です。
m = map(str.upper, ["a"])
print(next(m))
print(next(m, "なし")) # 既定値を渡すとエラーにならない
実行結果:
A
なし
計算は取り出すときまで行われない(遅延評価)
map を作った時点では、関数はまだ1回も呼ばれていません。
def show(x):
print(" call", x)
return x * 10
m = map(show, [1, 2, 3])
print("made")
print(next(m))
実行結果:
made
call 1
10
「made」のあとで初めて「call 1」が出ています。この性質のため、map を作ったあとに元のリストを変更すると、変更後の内容が結果に反映されます。
data = [1, 2, 3]
m = map(lambda x: x * 2, data)
data.append(4)
print(list(m))
実行結果:
[2, 4, 6, 8]
その時点の結果を確定させたいときは、すぐに list() で包んでおきます。
メモリの使い方にも違いが出ます。100万件を2倍にした結果を、リストとイテレータで比べました(sys.getsizeof の値、単位はバイト)。
| 書き方 | Python 3.13.7 | Python 3.14.7 |
|---|---|---|
[x * 2 for x in range(1_000_000)] |
8,448,728 | 8,448,728 |
map(lambda x: x * 2, range(1_000_000)) |
48 | 56 |
(x * 2 for x in range(1_000_000)) |
200 | 208 |
リストは約8MB(要素への参照のぶんで、中身の数値は別に必要)を確保しますが、map やジェネレータ式は数十〜200バイト程度です。合計や件数だけがほしい大量データでは、リストを作らずイテレータのまま渡すとメモリを節約できます。
複数のリストを同時にmapする
map にイテラブルを2つ以上渡すと、それぞれから1個ずつ取り出して関数に渡します。 関数は渡したイテラブルの数だけ引数を受け取る必要があります。
from operator import add
print(list(map(lambda a, b: a + b, [1, 2, 3], [10, 20, 30])))
print(list(map(add, [1, 2, 3], [10, 20, 30]))) # operator.add でも同じ
print(list(map(pow, [2, 3, 4], [2, 2, 2]))) # 2乗
print([a + b for a, b in zip([1, 2, 3], [10, 20, 30])]) # 内包表記 + zip
実行結果:
[11, 22, 33]
[11, 22, 33]
[4, 9, 16]
[11, 22, 33]
長さが違うときは、短いほうに合わせて止まります。
print(list(map(lambda a, b: a + b, [1, 2, 3], [10, 20])))
実行結果:
[11, 22]
引数の数とイテラブルの数が合わないと、次のエラーになります。
print(list(map(lambda a, b: a + b, [1, 2, 3])))
実行結果:
TypeError: <lambda>() missing 1 required positional argument: 'b'
Python 3.14 では strict=True で長さの違いを検出できる
Python 3.14 で、map に strict 引数が追加されました。strict=True を付けると、長さがそろっていないときに ValueError で知らせてくれます。
from operator import add
print(list(map(add, [1, 2], [10, 20], strict=True)))
print(list(map(add, [1, 2, 3], [10, 20], strict=True)))
実行結果(Python 3.14.7):
[11, 22]
ValueError: map() argument 2 is shorter than argument 1
同じコードを Python 3.13.7 で実行すると、次のエラーになります。
TypeError: map() takes no keyword arguments
公式ドキュメントの map の項にも「バージョン 3.14 で変更: strict 引数が追加されました」という趣旨の記載があり、今回開いて確認しました。3.13 以前では、zip の strict 引数と内包表記を組み合わせると同じ確認ができます。
print([a + b for a, b in zip([1, 2, 3], [10, 20], strict=True)])
実行結果(3.13.7 と 3.14.7 で同じ):
ValueError: zip() argument 2 is shorter than argument 1
長いほうに合わせたい・組になったデータを渡したい
from itertools import starmap, zip_longest
# タプルの中身を引数として展開して渡す
print(list(starmap(pow, [(2, 3), (3, 2)])))
# 長いほうに合わせ、足りないぶんは None になる
pairs = zip_longest([1, 2, 3], [10, 20])
print(list(map(lambda p: (p[0] or 0) + (p[1] or 0), pairs)))
実行結果:
[8, 9]
[11, 22, 3]
リスト以外に使う:辞書・文字列・集合
map と filter はイテラブルなら何にでも使えます。結果を受け取る側の型を変えるだけです。
# 文字列:1文字ずつ処理される
print(list(filter(lambda c: c.isalpha(), "a1b2")))
print("".join(filter(str.isalpha, "a1b2")))
# 辞書:items() を渡し、dict() で受け取る
prices = {"pen": 120, "note": 300, "bag": 2500}
print(dict(filter(lambda kv: kv[1] >= 300, prices.items())))
print({k: v for k, v in prices.items() if v >= 300}) # 辞書内包表記
print(dict(map(lambda kv: (kv[0], kv[1] * 2), prices.items())))
# 集合・タプルで受け取る
print(set(map(abs, [-1, 1, 2])), tuple(map(abs, [-1, 2])))
# 集計関数や join にそのまま渡す
print(sum(map(len, ["ab", "cde"])), max(map(abs, [-5, 3])),
any(map(str.isdigit, ["a", "1"])), ",".join(map(str, [1, 2, 3])))
# 入力の文字列を数値に分ける定番の形
a, b = map(int, "3 5".split())
print(a, b)
実行結果:
['a', 'b']
ab
{'note': 300, 'bag': 2500}
{'note': 300, 'bag': 2500}
{'pen': 240, 'note': 600, 'bag': 5000}
{1, 2} (1, 2)
5 5 True 1,2,3
3 5
辞書の場合は、kv[0]、kv[1] と添え字で書く lambda より、{k: v for k, v in ...} の辞書内包表記のほうがキーと値の役割が読み取りやすくなります。
速度比較:100万件での実測値
「map と内包表記はどちらが速いか」は、渡す関数の種類で答えが変わります。 100万件の整数リストを使い、timeit で5回実行(1セットあたり5回の繰り返し)した最小値を取りました。測定は2回行い、2回目の値を載せています。回ごとに1〜3割ほど揺れることがあったので、細かな差ではなく大小の傾向としてご覧ください。
測定コードの形は次のとおりです。
import timeit
setup = """nums = list(range(1_000_000)); strs = [str(x) for x in nums]
def dbl(x): return x * 2"""
def r(stmt, n=5):
t = min(timeit.repeat(stmt, setup, number=n, repeat=5)) / n * 1000
print(f"{t:8.1f} ms {stmt}")
r("list(map(lambda x:x*2,nums))")
r("[x*2 for x in nums]")
| 処理 | 書き方 | Python 3.13.7 | Python 3.14.7 |
|---|---|---|---|
| 変換 | list(map(lambda x: x * 2, nums)) |
55.0 ms | 56.5 ms |
| 変換 | list(map(dbl, nums))(def の関数) |
56.4 ms | 56.4 ms |
| 変換 | [x * 2 for x in nums] |
39.3 ms | 38.9 ms |
| 変換 | [dbl(x) for x in nums] |
55.0 ms | 53.2 ms |
| 変換 | list(x * 2 for x in nums) |
50.8 ms | 49.1 ms |
| 変換 | for と append | 40.2 ms | 41.4 ms |
| 組み込み関数 | list(map(str, nums)) |
73.5 ms | 74.7 ms |
| 組み込み関数 | [str(x) for x in nums] |
78.8 ms | 82.1 ms |
| 組み込み関数 | list(map(int, strs)) |
67.6 ms | 60.2 ms |
| 組み込み関数 | [int(s) for s in strs] |
73.4 ms | 70.4 ms |
| 抽出 | list(filter(lambda x: x % 2 == 0, nums)) |
40.7 ms | 40.9 ms |
| 抽出 | [x for x in nums if x % 2 == 0] |
35.6 ms | 26.4 ms |
| 抽出 | list(filter(None, nums)) |
9.5 ms | 9.4 ms |
| 抽出 | [x for x in nums if x] |
18.2 ms | 16.2 ms |
| 抽出+変換 | list(map(lambda..., filter(lambda..., nums))) |
71.6 ms | 65.8 ms |
| 抽出+変換 | [x * 2 for x in nums if x % 2 == 0] |
46.9 ms | 40.4 ms |
| 合計 | sum(map(lambda x: x * 2, nums)) |
39.5 ms | 34.7 ms |
| 合計 | sum(x * 2 for x in nums) |
32.0 ms | 27.7 ms |
| 合計 | sum([x * 2 for x in nums]) |
40.1 ms | 41.5 ms |
この結果から読み取れる傾向は3つです。
- lambda を使う変換・抽出は、内包表記のほうが速い(変換で約3割、抽出+変換で約4割の差)。内包表記は式を直接実行しますが、map + lambda は要素ごとに関数呼び出しが発生するためです。同じ関数を呼ぶ
[dbl(x) for x in nums]とmap(dbl, nums)はほぼ同じ時間でした。 - str や int のような組み込み関数をそのまま渡すなら、map のほうがやや速いという結果でした。
filter(None, ...)は約9ミリ秒で、同じ意味の内包表記の半分ほどでした。空や 0 を取り除く用途では、短く書けて速い選択肢です。
100万件でも差は数十ミリ秒なので、ふだんは読みやすさを優先し、件数が多い処理で気になったときに測って選ぶ、という進め方がおすすめです。
どれを使うかの判断表
| 状況 | おすすめ | 例 |
|---|---|---|
| 既存の関数名をそのまま渡せる | map / filter | map(int, strs)、filter(str.isdigit, words) |
| 式や条件をその場で書く | 内包表記 | [x * 2 for x in nums if x > 0] |
| 抽出と変換を両方行う | 内包表記 | [o["price"] for o in orders if o["qty"] > 0] |
| 空・0・None をまとめて除く | filter(None, ...) |
list(filter(None, vals)) |
| sum・any・max・join に渡すだけ | ジェネレータ式 / map | sum(x * 2 for x in nums) |
| 結果を何度も使う・長さや添え字が必要 | リスト内包表記 | data = [f(x) for x in src] |
| 件数が非常に多く、順に処理するだけ | ジェネレータ式 / map | for row in map(parse, lines): |
| 複数のリストを要素ごとに組み合わせる | 複数引数の map / zip | map(add, xs, ys) |
| 条件が複雑で2行以上になる | def + for、または def + filter | 関数に名前を付けて渡す |
同じ処理を3通りで書いた例で、読みやすさを比べてみます。数量が1以上の注文について、小計(数量 × 単価)を求めます。
orders = [
{"item": "pen", "qty": 3, "price": 120},
{"item": "bag", "qty": 0, "price": 2500},
{"item": "note", "qty": 2, "price": 300},
]
# 内包表記
print([o["qty"] * o["price"] for o in orders if o["qty"] > 0])
# map + filter
print(list(map(lambda o: o["qty"] * o["price"],
filter(lambda o: o["qty"] > 0, orders))))
# 合計だけほしいならジェネレータ式を sum に渡す
print(sum(o["qty"] * o["price"] for o in orders if o["qty"] > 0))
実行結果:
[360, 600]
[360, 600]
960
内包表記は「何を作るか → どこから → どの条件で」の順に左から読めます。map + filter は内側の filter から先に読む必要があるため、抽出と変換を両方行う場面では内包表記が読みやすいことが多いです。
内包表記で使える書き方も2つ押さえておくと便利です。
nums = [1, 2, 3, 4, 5, 6]
print([x if x % 2 == 0 else 0 for x in nums]) # if-else は for の前(全要素を残して値を変える)
print([y for x in nums if (y := x * x) > 10]) # := で計算結果を使い回す
実行結果:
[0, 2, 0, 4, 0, 6]
[16, 25, 36]
for の後ろの if は「絞り込み」、前の if-else は「値の切り替え」です。位置で意味が変わります。
itertools・functoolsへの発展一覧
map と filter に慣れたら、同じ「イテレータを返す」仲間の関数も使えるようになります。どれも結果は1回きりのイテレータなので、表示には list() が必要です。
from functools import reduce
from itertools import (chain, islice, takewhile, dropwhile, accumulate,
compress, pairwise, batched, count)
from operator import mul
nums = [1, 2, 3, 4, 5, 6]
print(reduce(lambda acc, x: acc + x, nums), reduce(mul, nums, 1), sum(nums))
print(list(chain([1, 2], [3])), list(chain.from_iterable([[1, 2], [3]])))
print(list(islice(map(lambda x: x * x, range(10**9)), 3)))
print(list(takewhile(lambda x: x < 4, nums)), list(dropwhile(lambda x: x < 4, nums)))
print(list(accumulate(nums)), list(compress("abcd", [1, 0, 1, 0])))
print(list(pairwise([1, 2, 3])), list(batched(nums, 4)))
print(list(islice(filter(lambda x: x % 7 == 0, count(1)), 3)))
実行結果:
21 720 21
[1, 2, 3] [1, 2, 3]
[0, 1, 4]
[1, 2, 3] [4, 5, 6]
[1, 3, 6, 10, 15, 21] ['a', 'c']
[(1, 2), (2, 3)] [(1, 2, 3, 4), (5, 6)]
[7, 14, 21]
3行目の range(10**9)(10億件)に対する map が一瞬で終わるのは、遅延評価のおかげで、islice が取り出した先頭3件しか計算していないためです。
| 関数 | 役割 | map / filter との関係 |
|---|---|---|
functools.reduce(f, it, 初期値) |
要素を順に畳み込んで1つの値にする | 合計は sum、最大は max のほうが簡潔 |
itertools.starmap(f, it) |
タプルを展開して関数に渡す | 複数引数の map の別形 |
itertools.filterfalse(f, it) |
条件に合わない要素を残す | filter の反対 |
itertools.takewhile(f, it) |
条件が真の間だけ先頭から取り出す | 途中で打ち切る filter |
itertools.dropwhile(f, it) |
条件が真の間は読み飛ばし、残りを返す | takewhile の反対 |
itertools.compress(data, flags) |
真偽のリストで絞り込む | 判定結果が別のリストにあるとき |
itertools.islice(it, n) |
先頭 n 件だけ取り出す | イテレータは添え字が使えないため |
itertools.chain(a, b) |
複数のイテラブルをつなぐ | map の入力をまとめる |
itertools.accumulate(it) |
累積の値を順に返す | 途中経過が見える reduce |
itertools.pairwise(it) |
隣り合う2つを組にする | 差分の計算に便利(3.10 以降) |
itertools.batched(it, n) |
n 個ずつのまとまりに分ける | 分割して処理する(3.12 以降) |
itertools.zip_longest(a, b) |
長いほうに合わせて組にする | 長さが違う複数リストの map |
reduce は、空のイテラブルに初期値なしで使うと次のエラーになります。
from functools import reduce
from operator import add
reduce(add, [])
実行結果:
TypeError: reduce() of empty iterable with no initial value
reduce(add, [], 0) のように初期値を渡しておくと、空でも 0 が返ります(実行して確認済みです)。
よくある質問
Q. map と内包表記はどちらを使うべきですか?
A. 既存の関数名をそのまま渡せるなら map、式や条件をその場で書くなら内包表記が読みやすく、速度面でも有利でした。今回の測定では、lambda を使う変換は内包表記が約3割速く、map(int, strs) のように組み込み関数を渡す場合は map がやや速い結果でした。
Q. map の結果を print したら <map object at 0x...> と出ます。どうすればよいですか?
A. print(list(map(...))) のように list() で包むと中身が表示されます。map はイテレータを返すため、そのまま表示するとオブジェクトの種類と場所が出ます。エラーではありません。
Q. filter の結果を2回使ったら、2回目が空になりました。
A. filter や map の結果は1回取り出すと空になります。最初に result = list(filter(...)) とリストにしておけば、何度でも使えます。
Q. lambda の中に複数の処理を書けますか? A. lambda に書けるのは式1つだけです。処理が2つ以上あるときは def で関数を定義し、その名前を map や filter に渡します。名前が付くぶん、あとから読む人にも意図が伝わりやすくなります。
まとめ
- map は変換、filter は抽出です。どちらもイテレータを返すので、中身を見るには list() で包みます。
<map object at ...>はエラーではなく、取り出し待ちの表示です。sum・join・for に渡すだけなら list() は不要です。- イテレータは1回使うと空になります。 何度も使う結果は、最初にリストにしておきます。
- lambda は式1つの関数です。1回だけ渡す短い処理に使い、2行以上になるなら def にします。
- 速度は「lambda を使うなら内包表記」「組み込み関数を渡すなら map」が有利でした。
filter(None, ...)は空の除去に短く速く使えます。 - 複数のリストは
map(f, xs, ys)で同時に処理でき、Python 3.14 ではstrict=Trueで長さの違いを検出できます。 - 慣れてきたら、itertools の takewhile・islice・chain・batched などに広げると、書ける処理の幅が増えます。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python map・filterの使い方|lambdaと内包表記の違い早見表」
株式会社RJCでは、Pythonのmap・filterや内包表記の使い分けを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。