高階関数と Lambda
Python では、関数も「値」です——変数に代入したり、他の関数に渡したりすることが、数値や文字列と同じようにできます。これにより非常に簡潔でエレガントなコードが可能になります。高階関数は、他の関数を引数として受け取ったり、関数を返したりする関数です。このレッスンはあなたに新しい世界を開くでしょう。
1. 関数は第一級オブジェクト
Python では、関数は他の値と同じように操作できます:
例:関数を値として扱う
def say_hello(name):
return f"Hello, {name}"
# 関数を変数に代入
my_func = say_hello
print(my_func("Alice")) # Hello, Alice
# 関数を引数として渡す
def greet_with(func, name):
return func(name)
print(greet_with(say_hello, "Bob")) # Hello, Bob
# 関数から関数を返す
def create_greeter(greeting):
def greeter(name):
return f"{greeting}, {name}!"
return greeter
say_hi = create_greeter("Hi")
say_hello = create_greeter("Hello")
print(say_hi("Alice")) # Hi, Alice!
print(say_hello("Bob")) # Hello, Bob!
2. map():一括変換
map() はシーケンス内のすべての要素に関数を適用し、新しいイテレータを返します:
例:map による一括変換
# リスト内の各数値を2乗
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 文字列リストを大文字に変換
fruits = ["apple", "banana", "cherry"]
upper_fruits = list(map(str.upper, fruits))
print(upper_fruits) # ['APPLE', 'BANANA', 'CHERRY']
# 摂氏から華氏へ
temps_c = [0, 10, 20, 30, 40]
temps_f = list(map(lambda c: c * 9 / 5 + 32, temps_c))
print(temps_f) # [32.0, 50.0, 68.0, 86.0, 104.0]
map()はリスト内包表記と同等:map(f, seq)≈[f(x) for x in seq]。どちらを使うかは好みの問題です——内包表記の方がより「Python らしい」です。
3. filter():一括フィルタリング
filter() は関数が True を返す要素を選択します:
例:filter によるデータ抽出
# 偶数のみ抽出
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# 長さ 5 以上の単語を抽出
words = ["cat", "elephant", "dog", "giraffe", "ant"]
long_words = list(filter(lambda w: len(w) >= 5, words))
print(long_words) # ['elephant', 'giraffe']
# 空でない文字列を抽出
data = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, data)) # None は「falsy な値を除去」を意味
print(non_empty) # ['hello', 'world', 'python']
filter()も内包表記と同等:filter(f, seq)≈[x for x in seq if f(x)]。関数としてNoneを使うと、すべての falsy な値(""、0、None、Falseなど)を除去することと同等です。
4. 高度な sorted() の使い方
レッスン 11 では sorted() の key パラメータを学びました。さらに深く掘り下げましょう:
例:sorted を使ったカスタムソート
# オブジェクト属性でソート
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78},
]
# スコア降順でソート
ranked = sorted(students, key=lambda s: s["score"], reverse=True)
for s in ranked:
print(f"{s['name']}: {s['score']}")
# 名前の長さでソート
sorted_by_name = sorted(students, key=lambda s: len(s["name"]))
print([s["name"] for s in sorted_by_name]) # ['Alice', 'Bob', 'Charlie']
例:マルチレベルソート(難易度 ⭐⭐)
# スコア降順、次に名前のアルファベット順でソート
data = [
("Alice", 85),
("Bob", 92),
("Charlie", 85),
("Diana", 92),
]
def sort_key(item):
# タプルで返す:まずスコア降順(負の数で反転)、次に名前
return (-item[1], item[0])
sorted_data = sorted(data, key=sort_key)
for name, score in sorted_data:
print(f"{name}: {score}")
出力:
Bob: 92
Diana: 92
Alice: 85
Charlie: 85
5. reduce():累積計算
reduce() はシーケンス内の要素を累積し、一度に 2 つの要素に関数を適用します。組み込み関数ではなく、functools からインポートします:
例:reduce による累積計算
from functools import reduce
# すべての数値の積
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120 (1×2×3×4×5)
# 最大値を求める
max_val = reduce(lambda a, b: a if a > b else b, [3, 1, 4, 1, 5])
print(max_val) # 5
# 文字列を連結
words = ["Hello", " ", "World", " ", "Python"]
sentence = reduce(lambda a, b: a + b, words)
print(sentence) # Hello World Python
reduce() の実行過程
# reduce(lambda x, y: x + y, [1, 2, 3, 4])
# ステップ 1:x=1, y=2 → 3
# ステップ 2:x=3, y=3 → 6
# ステップ 3:x=6, y=4 → 10
# 結果:10
reduce() vs ループ: reduce() でできることは for ループでもできます。reduce() はより簡潔で宣言的です——「どうやるか」ではなく「何をするか」を記述します。欠点は、初心者には理解が難しいことです。
6. Lambda 式の深掘り
Lambda はギリシャ文字の λ にちなんで名付けられ、本質的には無名関数——名前のない小さな関数で、使ったら捨てられます。
Lambda と通常関数の比較
例:Lambda vs 通常関数
# 通常の関数
def double(x):
return x * 2
# Lambda 式
double = lambda x: x * 2
print(double(5)) # 10
完全な Lambda 構文
lambda parameters: expression
# ↑ 以下と同等
def anonymous_function(parameters):
return expression
複数パラメータ
例:複数パラメータの Lambda
add = lambda a, b: a + b
print(add(3, 5)) # 8
# Lambda 内での三項演算子
max_val = lambda a, b: a if a > b else b
print(max_val(10, 20)) # 20
Lambda を使うタイミング
例:Lambda のユースケース
# ✅ 良い使い方:高階関数の引数としてのシンプルな 1 行変換
numbers = [1, 2, 3, 4]
result = list(map(lambda x: x ** 2, numbers))
# ❌ 悪い使い方:複数行必要な複雑なロジック — 通常の def 関数を使用
よくあるユースケース
- データパイプライン:
map()→filter()→sorted()を連鎖させてデータ処理 - カスタムソート:
lambdaをkeyパラメータとして様々なソートルールに使用 - イベントコールバック:GUI や Web フレームワークに関数を引数として渡す
- クロージャファクトリ:外側の関数が内側の関数を返して「パラメータ化された」機能を実現
- 遅延評価:高階関数が必要になるまで計算を延期
❓ よくある質問
map() とリスト内包表記、どちらが優れていますか?map() の利点:① 既存の関数名がある場合に短い(map(str.upper, seq) vs [s.upper() for s in seq])。② 遅延イテレータを返すため、大規模データでメモリを節約。ほとんどの場合、リスト内包表記が良い選択です。reduce() は現代の Python でも使われていますか?reduce() を好んでおらず、理解が難しいと考えていました。ほとんどの reduce() のシナリオはループやより直接的な関数(sum()、max() など)で置き換えられます。ただし、reduce() は関数型プログラミングにおける重要な概念です。📖 まとめ
- Python では関数は第一級オブジェクト——代入、受け渡し、返却が可能
map(f, seq)は各要素に関数を適用。list()でリストに変換filter(f, seq)は関数が True を返す要素を選択sorted(seq, key=f)はカスタムソートに関数を受け入れreduce(f, seq)は値を累積。functoolsからインポート- Lambda
lambda params: expressionはシンプルなシナリオ向けの無名 1 行関数
📝 練習問題
-
基本(難易度 ⭐):
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]として、map()とfilter()を使って:奇数のみを抽出し、その平方を計算してください。ヒント:最初にfilterで奇数を抽出、次にmapで平方を計算。 -
中級(難易度 ⭐⭐):辞書のリスト
products = [{"name": "A", "price": 100}, {"name": "B", "price": 80}, {"name": "C", "price": 120}]が与えられたとき、sorted()を使って価格の降順でソートし、名前と価格を出力してください。 -
挑戦(難易度 ⭐⭐⭐):
reduce()を使ってflatten(nested_list)関数を実装してください。ネストしたリストを 1 レベル平坦化します。例:flatten([[1, 2], [3, 4], [5]])は[1, 2, 3, 4, 5]を返します。ヒント:reduceで毎回+を使って 2 つのリストを結合。次に同じものをリスト内包表記で実装し、アプローチを比較してください。



