短い処理を一時的に関数化したいとき、Pythonのラムダ式は強力な選択肢になります。
本記事では、ラムダ式の基本から、map、filter、sortedでの実践的な使い方まで、初心者の方が段階を踏んで理解できるよう丁寧に解説します。
可読性やデバッグのコツも紹介します。
Pythonのラムダ式とは
ラムダ式の意味と用途
ラムダ式とは、名前を付けずにその場で関数を作るための記法(匿名関数)です。
主に一時的なコールバック関数や、簡単な変換関数を渡す場面で使われます。
たとえば、sorted
のkey
引数や、map
、filter
などの高階関数に渡すときに、わざわざdef
で関数定義を用意せず、その場で短い処理を記述できます。
基本構文
ラムダ式は1つの「式(expression)」で結果を返します。
文(statement)は書けないため、シンプルな処理に向きます。
lambda 引数: 式
次の書式で書きます。
# 例: 受け取った数値を2倍にするラムダ
double = lambda x: x * 2
print(double(10)) # 20
20
引数が複数ある場合も同様です。
# 例: 2つの数値を足すラムダ
add = lambda a, b: a + b
print(add(3, 5)) # 8
8
defとの違いと使い分け
ラムダ式とdef
の主な違いを整理します。
- ラムダ式は式ベースで短く書けますが、1つの式しか書けません。
def
は複文やreturn
、docstring
、型アノテーションなど、通常の関数の機能をすべて使えます。- デバッグ時のトレースバックでは、ラムダ式は
<lambda>
としか表示されないため、識別しづらいことがあります。 - 可読性の観点から、処理が複雑になったら
def
に移行するのが定石です。
以下は違いをまとめた比較表です。
観点 | lambda | def |
---|---|---|
構文 | 1つの式のみ | 複数行・複文が書ける |
使いどころ | 短い一時関数 | 再利用・説明が必要な関数 |
戻り値 | 式の評価結果 | return で任意に制御 |
デバッグ | 名称は<lambda> で追いにくい | 関数名で追いやすい |
型ヒント | 直接は不可 | 可能(パラメータ/戻り値) |
できることと制限
ラムダ式は簡潔で便利ですが、制限があります。
代表的なポイントを押さえましょう。
- 1つの式しか書けません。代入文やfor文、if文(文としてのif)などの複文は不可です。
return
は書けません。式の評価結果がそのまま戻り値になります。- 例外処理の
try
/except
、with
文なども書けません。 - 型アノテーション(例:
x: int
)をラムダの引数に直接書くことはできません。型を付けたい場合はdef
を使うか、Callable
で変数側を注釈します。
以下はNG例です。
# NG: returnは使えません(構文エラー)
# bad = lambda x: return x * 2
# NG: 文は書けません(構文エラー)
# bad = lambda x: for i in range(x): print(i)
map filter sortedで使うラムダ式の実例
mapとラムダ式の基本
map(func, iterable)
は、各要素に関数を適用して新しいイテラブルを返します。
ラムダ式を使うと、その場で変換ロジックを書けます。
# 例: リストの各要素を2乗にする
nums = [1, 2, 3, 4]
squares = map(lambda x: x * x, nums) # mapはイテレータを返す
print(list(squares)) # 出力のためにlist化
# 例: 文字列リストを大文字化
words = ["apple", "Banana", "cherry"]
upper_words = map(lambda s: s.upper(), words)
print(list(upper_words))
[1, 4, 9, 16]
['APPLE', 'BANANA', 'CHERRY']
補足として、Pythonicには内包表記でも表現可能です。
可読性重視なら内包表記、既存APIのコールバックに渡すならラムダ、と使い分けると良いです。
filterとラムダ式の基本
filter(func, iterable)
は、関数が真と判定した要素だけを通します。
ラムダ式で判定条件を短く書けます。
# 例: 偶数だけを抽出する
nums = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens))
# 例: Truthinessでフィルタ(空文字やNoneを除去)
items = ["hello", "", "world", None, " "]
# boolに変換して真のものだけ残す
truthy_items = filter(lambda x: bool(x), items)
print(list(truthy_items))
[2, 4, 6]
['hello', 'world', ' ']
filter(None, iterable)
とすると、真の値だけを残す簡略記法になります。
items = ["", "A", 0, 1, None, "ok"]
print(list(filter(None, items))) # Truthyな要素だけ
['A', 1, 'ok']
sortedのkeyにラムダ式を使う
sorted(iterable, key=...)
やlist.sort(key=...)
では、要素からソート基準を取り出す関数(キー関数)を指定します。
ラムダ式の定番用途です。
# 例: 文字列を長さでソート
words = ["pear", "apple", "banana", "kiwi"]
print(sorted(words, key=lambda s: len(s)))
# 例: 大文字小文字を無視して辞書順ソート
mixed = ["Zoo", "alpha", "Bravo"]
print(sorted(mixed, key=lambda s: s.lower()))
['kiwi', 'pear', 'apple', 'banana']
['alpha', 'Bravo', 'Zoo']
複数キーでソートする例: タプルを返すラムダ
複数の基準で並べたいときは、キー関数でタプルを返します。
Pythonはタプルを辞書式順序で比較します。
# 例: (名前, 年齢)のタプルを「年齢昇順→名前昇順」でソート
people = [("Taro", 20), ("Jiro", 20), ("Hanako", 18)]
sorted_people = sorted(people, key=lambda x: (x[1], x[0]))
print(sorted_people)
# 例: 「年齢降順→名前昇順」
# 年齢のみ負にして降順を実現(数値なら有効なテクニック)
sorted_people_desc_age = sorted(people, key=lambda x: (-x[1], x[0]))
print(sorted_people_desc_age)
[('Hanako', 18), ('Jiro', 20), ('Taro', 20)]
[('Taro', 20), ('Jiro', 20), ('Hanako', 18)]
もちろん、sorted(..., reverse=True)
で全体を逆順にもできますが、キーごとに昇降を混在させたい場合はタプル中で符号を工夫すると表現しやすいです。
辞書リストをソートする例: キー関数で指定
実務では辞書のリストをソートすることが多いです。
キー関数で辞書の特定キーを取り出します。
# 例: 社員情報を年齢→名前で昇順ソート
employees = [
{"name": "Alice", "age": 30, "dept": "Sales"},
{"name": "Bob", "age": 25, "dept": "Dev"},
{"name": "Charlie", "age": 25, "dept": "Dev"},
]
by_age_name = sorted(employees, key=lambda e: (e["age"], e["name"]))
print(by_age_name)
[{'name': 'Bob', 'age': 25, 'dept': 'Dev'}, {'name': 'Charlie', 'age': 25, 'dept': 'Dev'}, {'name': 'Alice', 'age': 30, 'dept': 'Sales'}]
標準ライブラリoperator
のitemgetter
やattrgetter
を使うと、読みやすく高速な場合があります。
# itemgetterの例(辞書から複数キーを取り出す)
from operator import itemgetter
by_age_name_fast = sorted(employees, key=itemgetter("age", "name"))
print(by_age_name_fast)
[{'name': 'Bob', 'age': 25, 'dept': 'Dev'}, {'name': 'Charlie', 'age': 25, 'dept': 'Dev'}, {'name': 'Alice', 'age': 30, 'dept': 'Sales'}]
ネストした値を取り出す例: ラムダで経路指定
ネストが深い辞書からキーをたどってソートしたいこともあります。
無防備にe["profile"]["score"]
のように書くと、欠損時にKeyError
になります。
get
を連鎖して安全に取り出す方法が実用的です。
# 例: ネストしたスコアでソート(欠損は0扱い)
users = [
{"name": "Alice", "profile": {"score": 80}},
{"name": "Bob", "profile": {}}, # score欠損
{"name": "Charlie", "profile": {"score": 95}},
]
safe_score = lambda u: u.get("profile", {}).get("score", 0)
print(sorted(users, key=safe_score, reverse=True))
[{'name': 'Charlie', 'profile': {'score': 95}}, {'name': 'Alice', 'profile': {'score': 80}}, {'name': 'Bob', 'profile': {}}]
ネストが更に深い場合も、get
を重ねるか、専用の取得関数をdef
で用意すると読みやすさが向上します。
初心者が押さえるベストプラクティス
可読性を保つコツ
ラムダ式は短さが魅力ですが、短くても読みやすさが最優先です。
引数名は意味がわかる名前にし、式の中で過剰なロジックを詰め込まないようにします。
sorted(key=...)
のような「意図が明確なコールバック」に限定して使うと、第三者にも通じやすくなります。
また、似た用途ではoperator.itemgetter
やattrgetter
の利用も検討できます。
ラムダを使わない方が良いケース
処理が複雑、条件分岐が多い、例外処理が必要、または型ヒントやドキュメント文字列を付けたい場合はdef
で関数化した方が良いです。
スタックトレースに名前が出るため、運用時の調査や保守も容易です。
1行を超えそうになったらdef
への切り替えを検討してください。
デバッグと例外の注意
ラムダ式はトレースバック上で<lambda>
と表示され、どのラムダか判別しづらいことがあります。
デバッグ中は、ラムダを一時的にdef
へ置き換える、あるいはラムダを変数に代入して説明的な名前を付けると追跡しやすくなります。
例外は通常どおり上位へ伝播しますが、ラムダの中に過剰な副作用を持ち込まず、「入力を受け取り値を返す純粋な関数」に近づけるとバグを減らせます。
# デバッグしやすい命名
key_by_safe_score = lambda u: u.get("profile", {}).get("score", 0)
コールバック関数での活用ポイント
ラムダ式は「小さな関数をコールバックとして渡す」用途に強いです。
sorted(key=...)
、map
、filter
のほか、re.sub(pattern, repl, s)
のrepl
に関数を渡して動的に置換文字列を決める、といった応用もあります。
短く明確なロジックをその場で書けるため、関数の定義場所へ移動する手間がなく、コードの流れを追いやすくなります。
逆に、複数箇所で再利用する処理はdef
にまとめた方が保守的です。
まとめ
ラムダ式は「短い処理をその場で関数化する」ための記法で、map
、filter
、sorted(key=...)
のような高階関数と非常に相性が良いです。
1つの式しか書けない、return
や複文が使えない、型アノテーションを直接付けられないなどの制約はありますが、その分シンプルで読みやすい場面に最適です。
複雑化したらdef
へ切り替える、itemgetter
などと使い分ける、ネスト取り出しは安全に、という基本を押さえれば、Pythonicにコーディング効率を高められます。