Pythonの高階関数とは?map・filter・reduceとsortedのkey、関数を返す関数(デコレータ)を実例で解説
高階関数とは、「関数を引数として受け取る関数」または「関数を戻り値として返す関数」のことです。Pythonでは map()・filter()・sorted()・max() や functools.reduce() が代表的な高階関数で、デコレータも高階関数のしくみで作られています。
この記事では、高階関数の意味を自分で作る例から確かめ、map・filter・reduceの使い方と内包表記との使い分け、sortedのkey、関数を返す関数(クロージャ)とデコレータまでを、実行結果つきで説明します。コードはPython 3.12.15で実行しています(Python 3.14の新機能の例だけ3.14.8で実行)。
高階関数とは
Pythonでは、関数も数値や文字列と同じ「オブジェクト」です。変数に入れたり、別の関数に渡したり、関数の戻り値にしたりできます。この性質を使って、
- 関数を引数として受け取る関数
- 関数を戻り値として返す関数
のどちらか(または両方)にあてはまる関数を「高階関数」と呼びます。英語では higher-order function です。まずは自分で作ってみます。
# 1. 関数を引数に受け取る
def apply_twice(func, x):
return func(func(x))
def add3(n):
return n + 3
print(apply_twice(add3, 10))
print(apply_twice(str.upper, "py"))
# 2. 関数を戻り値として返す
def make_multiplier(k):
def multiply(n):
return n * k
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5), triple(5))
print(double.__name__)
16
PY
10 15
multiply
apply_twiceは、受け取った関数funcを2回適用する高階関数です。add3を渡せば10+3+3で16、文字列のメソッドstr.upperを渡せば大文字にした結果が返ります。関数を渡すときはadd3()ではなくadd3と、かっこを付けずに書きます。かっこを付けると、関数そのものではなく呼び出した結果を渡してしまいます。make_multiplierは、関数の中で関数multiplyを作って返す高階関数です。doubleとtripleは、それぞれ「2倍する関数」「3倍する関数」になります。
高階関数を使うと、「何をするか」の部分を関数として外から差し替えられるようになり、同じ処理の骨組みを使い回せます。
Pythonに組み込まれている主な高階関数
| 関数 | 受け取る関数の役割 | 結果 |
|---|---|---|
map(関数, イテラブル) | 各要素を変換する | 変換後の値を順に返すイテレータ |
filter(関数, イテラブル) | 各要素を残すか判定する | 真と判定された要素を返すイテレータ |
functools.reduce(関数, イテラブル[, 初期値]) | 2つの値を1つにまとめる | 1つの値 |
sorted(イテラブル, key=関数) | 並べ替えの基準を作る | 並べ替えた新しいリスト |
max() / min()(key=関数) | 比べる基準を作る | 最大・最小の要素 |
functools.partial(関数, 引数…) | 引数の一部を固定する元の関数 | 新しい関数 |
map・filter・sorted・max・minは組み込み関数、reduceとpartialは標準ライブラリの functools にあります(組み込み関数、functools)。
map・filter・reduceの使い方
from functools import reduce
prices = [980, 1500, 300, 2200, 640]
m = map(lambda p: int(p * 1.1), prices)
print(m)
print(list(m))
print(list(m)) # 2回目は空になる
print(list(filter(lambda p: p >= 1000, prices)))
print(reduce(lambda acc, p: acc + p, prices))
print(reduce(lambda acc, p: acc + p, prices, 0) == sum(prices))
# 内包表記で書くと
print([int(p * 1.1) for p in prices])
print([p for p in prices if p >= 1000])
# 複数のイテラブルを渡す map
print(list(map(lambda a, b: a * b, [1, 2, 3], [10, 20, 30])))
# filter(None, ...) は偽の値を取り除く
print(list(filter(None, [0, 1, "", "a", None, [], [2]])))
<map object at 0x105c62d70>
[1078, 1650, 330, 2420, 704]
[]
[1500, 2200]
5620
True
[1078, 1650, 330, 2420, 704]
[1500, 2200]
[10, 40, 90]
[1, 'a', [2]]
(0x105c62d70 の部分は実行するたびに変わります)
map:すべての要素を変換する
map() はリストではなく「mapオブジェクト」というイテレータを返します。中身を見るには list() で変換します。イテレータは一度最後まで取り出すと空になるので、同じ m を2回 list() すると2回目は [] になります。何度も使う結果は、最初にリストにしておきます。
関数が引数を2つ取るなら、イテラブルを2つ渡せます([1, 2, 3] と [10, 20, 30] の要素どうしを掛けて [10, 40, 90])。
filter:条件に合う要素だけ残す
filter() は、関数が真を返した要素だけを残します。関数の代わりに None を渡すと、0・空文字・None・空のリストのような「偽」とみなされる値が取り除かれます。
reduce:全体を1つの値にまとめる
reduce() は、前から順に2つずつ関数を適用して1つの値にまとめます。上の例は ((((980+1500)+300)+2200)+640) と計算されます。合計なら sum() で足りますが、「辞書を順番に合成する」のように専用の関数がない処理をまとめるときに使います。
空のリストを渡すと、初期値がない場合はエラーになります。
from functools import reduce
try:
reduce(lambda a, b: a + b, [])
except TypeError as e:
print("TypeError:", e)
print(reduce(lambda a, b: a + b, [], 0))
TypeError: reduce() of empty iterable with no initial value
0
空になる可能性があるデータには、3つ目の引数に初期値を渡しておきます。
内包表記とどちらを使うか
上の例の後半のように、map・filterと同じことはリスト内包表記でも書けます。
| やりたいこと | 高階関数 | 内包表記 |
|---|---|---|
| 変換 | list(map(lambda p: int(p * 1.1), prices)) | [int(p * 1.1) for p in prices] |
| 絞り込み | list(filter(lambda p: p >= 1000, prices)) | [p for p in prices if p >= 1000] |
| 変換+絞り込み | list(map(f, filter(g, xs))) | [f(x) for x in xs if g(x)] |
使い分けの目安です。
- lambdaを書く必要があるなら内包表記のほうが読みやすいことが多い
- すでに名前のある関数を当てはめるだけなら map が短い(例:
list(map(int, ["1", "2"]))、list(map(str.strip, lines))) - 大量のデータを少しずつ処理したいなら、イテレータを返す map・filter(またはジェネレータ式)にすると、全体をメモリに載せずに済む
lambda(無名関数)の書き方はPythonのlambdaとは?で詳しく解説しています。
sorted・maxのkeyも高階関数
並べ替えや最大値の「基準」を関数で渡すのも、高階関数の代表的な使い方です。
from operator import itemgetter
members = [
{"name": "佐藤", "age": 18, "score": 72},
{"name": "鈴木", "age": 34, "score": 88},
{"name": "高橋", "age": 21, "score": 88},
]
# 年齢の若い順
print([m["name"] for m in sorted(members, key=lambda m: m["age"])])
# 点数の高い順、同点なら年齢の若い順
print([m["name"] for m in sorted(members, key=lambda m: (-m["score"], m["age"]))])
# いちばん点数の高い人(同点なら先に出てくる人)
print(max(members, key=itemgetter("score"))["name"])
['佐藤', '高橋', '鈴木']
['高橋', '鈴木', '佐藤']
鈴木
key=に渡した関数が、各要素から「比べるための値」を作ります。- 複数の条件で並べたいときは、タプルを返します。数値を逆順にしたい部分だけ
-m["score"]のようにマイナスを付けています。 operator.itemgetter("score")はlambda m: m["score"]と同じ働きをする関数を作ります。- 同じ点数が複数あるとき、
max()は最初に出てきた要素を返します。
関数を返す高階関数(クロージャ・partial・デコレータ)
クロージャ
最初の make_multiplier のように、内側の関数が外側の変数(k)を覚えたまま返される仕組みを「クロージャ」と呼びます。設定値ごとに少しずつ違う関数を作りたいときに便利です。
functools.partial
関数の引数の一部を固定した、新しい関数を作ります。
from functools import partial
to_int_from_binary = partial(int, base=2) # int(x, base=2) を固定した関数
print(to_int_from_binary("1010"))
print(list(map(to_int_from_binary, ["1", "11", "111"])))
10
[1, 3, 7]
作った関数はそのまま map() に渡せます。詳しくはfunctools.partial()とは?で解説しています。
デコレータ
デコレータは「関数を受け取り、機能を足した関数を返す」高階関数です。@名前 と書くと、total_squares = timer(total_squares) と同じ意味になります。
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__}: {time.perf_counter() - start:.3f}秒")
return result
return wrapper
@timer
def total_squares(n):
return sum(i * i for i in range(n))
print(total_squares(1_000_000))
print(total_squares.__name__)
total_squares: 0.037秒
333332833333500000
total_squares
(かかった時間は実行環境によって変わります)
@wraps(func) を付けておくと、デコレータを通したあとも関数名(__name__)などが元の関数のまま残ります。付けないと名前が wrapper になり、エラーの表示やデバッグで困ります(functools.wraps()とは?)。
高階関数でつまずきやすい点
ループの中で作ったlambdaが同じ値を返す
funcs = []
for i in range(3):
funcs.append(lambda: i)
print([f() for f in funcs])
funcs = []
for i in range(3):
funcs.append(lambda i=i: i)
print([f() for f in funcs])
[2, 2, 2]
[0, 1, 2]
lambdaの中の i は、作った時点の値ではなく、呼び出した時点の i を見に行きます。ループが終わったあとに呼ぶと、どれも最後の値 2 になります。lambda i=i: i のように引数の初期値として渡すと、作った時点の値が保存されます。
map(…, strict=True)(Python 3.14以降)
複数のイテラブルを渡した map() は、いちばん短いものに合わせて黙って止まります。Python 3.14で strict 引数が追加され、strict=True にすると長さが違うときにエラーで知らせてくれるようになりました。また、functools.reduce() の初期値を initial= とキーワードで渡せるようになったのも3.14からです(いずれも公式ドキュメントで確認(2026年10月15日確認))。
# Python 3.14 以降
print(list(map(lambda a, b: a + b, [1, 2, 3], [10, 20])))
try:
list(map(lambda a, b: a + b, [1, 2, 3], [10, 20], strict=True))
except ValueError as e:
print("ValueError:", e)
[11, 22]
ValueError: map() argument 2 is shorter than argument 1
3.13以前のPythonで strict=True を書くと TypeError になるので、使うPythonのバージョンに注意してください。
どこで役に立つか
- データの前処理:文字列のリストを数値に変える、空の値を取り除く、といった処理が1行で書けます。
- 並べ替えと検索:
sortedやmaxのkeyは、辞書のリストやクラスのリストを扱うときに毎日のように使います。 - 共通の処理の後付け:ログ出力、時間計測、権限チェックなどをデコレータでまとめると、本体のコードを変えずに機能を足せます。WebフレームワークのFlaskやFastAPIでも、URLと関数を結び付けるのにデコレータが使われています。
map・filter・reduceを使った関数型プログラミングの書き方はPython関数型プログラミング入門、関数型プログラミングの考え方そのものは関数型プログラミングとは?で解説しています。
よくある質問
Q. 高階関数とは何ですか?
関数を引数として受け取る関数、または関数を戻り値として返す関数のことです。Pythonではmap()、filter()、sorted()、max()、functools.reduce()などが代表例で、デコレータも高階関数のしくみで作られています。
Q. map()の結果をprintするとmap objectと表示されるのはなぜですか?
map()はリストではなくイテレータを返すためです。中身を見るにはlist(map(…))のようにリストに変換します。イテレータは一度最後まで取り出すと空になるので、何度も使う結果はリストにしておきます。
Q. mapとリスト内包表記はどちらを使うべきですか?
lambdaを書く必要があるなら内包表記のほうが読みやすいことが多く、int や str.strip のようにすでにある関数を当てはめるだけならmapが短く書けます。大量のデータを少しずつ処理したいときは、イテレータを返すmapやジェネレータ式が向いています。
Q. reduce()で「reduce() of empty iterable with no initial value」と出るのはなぜですか?
空のリストなどを初期値なしで渡したためです。reduce(関数, データ, 0)のように3つ目の引数に初期値を渡すと、空のときはその値が返ります。
Q. デコレータと高階関数の関係は?
デコレータは関数を受け取り、機能を足した新しい関数を返す高階関数です。関数定義の前に@timerと書くと、total_squares = timer(total_squares)と書いたのと同じ意味になります。
あわせて読みたい
学習の順序はPython学習のロードマップ、標準で使える機能の一覧はPython標準ライブラリのモジュール一覧にまとめています。




