3秒でわかる
名前を付けずその場で作る小さな関数。Python では lambda 式、AWS では同名のサーバーレス実行サービスを指し、意味が 2 つに分かれます。
30秒図解
もう少し詳しく
どういうものか
lambda は 2 つの別々のものを指す。文脈で見分ける必要がある。
1 つは無名関数の構文である。Python では lambda に続けて引数と式を書き、名前を付けずにその場で関数を作る。名前の由来は 1930 年代のラムダ計算にさかのぼる。
もう 1 つは AWS Lambda というサービスで、サーバーを用意せずに関数単位でコードを実行できる仕組みを指す。イベントが発生したときだけ起動し、実行時間に対して課金される。
用語集としてはどちらも扱うが、以下は Python の lambda 式を中心に説明する。
なぜ必要か
関数を引数として渡す場面で、1 行の処理のために def で名前付き関数を定義すると、名前を考える手間が増え、使う場所と定義が離れて読みにくくなる。
# def で書くと、この 1 行のために名前が要る
def get_price(item):
return item["price"]
items.sort(key=get_price)
# lambda ならその場に書ける
items.sort(key=lambda item: item["price"])sorted min max の key、filter map の第 1 引数、pandas の apply などが典型的な使い所になる。
具体例
items = [
{"name": "コーヒー", "price": 480},
{"name": "紅茶", "price": 420},
{"name": "ケーキ", "price": 620},
]
# 価格の高い順に並べる
items.sort(key=lambda x: -x["price"])
# 500 円以上だけ取り出す
expensive = [x for x in items if x["price"] >= 500]
# 名前だけの一覧を作る
names = list(map(lambda x: x["name"], items))
# 引数を 2 つ取る形も書ける
from functools import reduce
total = reduce(lambda a, b: a + b, (x["price"] for x in items))
print(total) # 1520JavaScript の (x) => x * 2 というアロー関数も、役割としては同じ位置にある。
つまずきやすいところ
lambda 式には式しか書けない。if 文や for 文、代入、print 以外の複数行の処理は入らない。条件分岐を入れたい場合は下記のように三項演算子を使うが、これが長くなったら def に戻す方が読みやすい。
label = lambda x: "高" if x > 500 else "安"名前を付けて代入した時点で def の方が適切という判断もあり、実際 PEP 8 はそう勧めている。
ループ内で lambda を作るときの変数の捕捉も定番の罠になる。
fs = [lambda: i for i in range(3)]
print([f() for f in fs]) # [2, 2, 2] ── i は呼ぶ時点の値を見る
fs = [lambda i=i: i for i in range(3)]
print([f() for f in fs]) # [0, 1, 2] ── 既定値で束縛すれば意図どおりまた、f = lambda x: x * 2 のように lambda を変数へ代入する書き方は、PEP 8 が def を使うよう勧めている。名前を付けるなら def で良く、名前付き関数の方がトレースバックに関数名が出る分だけ調査しやすい。
似た用語との違い
無名関数、ラムダ式、アロー関数、クロージャは近い場所で使われる。前 3 つは書き方の名前で、クロージャは外側の変数を抱え込んだ関数という性質の名前である。lambda 式はクロージャになることも、ならないこともある。