Python高階関数完全マスター – map、filter、reduceから関数型プログラミングまで

 

Python高階関数は、関数を引数として受け取ったり、関数を戻り値として返したりする強力な機能です。本記事では、Pythonの高階関数の基本から応用まで、実践的なサンプルコードとともに詳しく解説します。

高階関数とは

高階関数(Higher-Order Function)は、以下の特徴を持つ関数です:

  • 他の関数を引数として受け取る
  • 関数を戻り値として返す
  • 関数を変数に代入して操作する

これにより、コードの再利用性と可読性が大幅に向上します。

基本的な高階関数

1. map()関数

リストの各要素に関数を適用して新しいリストを作成します。

# 基本的な使用例
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

# 関数を定義して使用
def double(x):
    return x * 2

doubled = list(map(double, numbers))
print(doubled)  # [2, 4, 6, 8, 10]

2. 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]

# 空文字列を除外
words = ["hello", "", "world", "", "python"]
filtered = list(filter(None, words))
print(filtered)  # ['hello', 'world', 'python']

3. reduce()関数

リストの要素を累積的に処理して単一の値を返します。

from functools import reduce

# 合計を計算
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15

# 最大値を取得
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum)  # 5

組み込み高階関数の活用

1. sorted()関数

# カスタムキーでソート
students = [("太郎", 85), ("花子", 92), ("次郎", 78)]
sorted_by_score = sorted(students, key=lambda x: x[1])
print(sorted_by_score)  # [('次郎', 78), ('太郎', 85), ('花子', 92)]

# 逆順ソート
reversed_sort = sorted(students, key=lambda x: x[1], reverse=True)
print(reversed_sort)  # [('花子', 92), ('太郎', 85), ('次郎', 78)]

2. max()とmin()関数

# カスタムキーで最大値・最小値を取得
words = ["python", "java", "javascript", "go"]
longest = max(words, key=len)
shortest = min(words, key=len)
print(f"最長: {longest}, 最短: {shortest}")  # 最長: javascript, 最短: go

3. any()とall()関数

# すべての要素が条件を満たすかチェック
numbers = [2, 4, 6, 8, 10]
all_even = all(x % 2 == 0 for x in numbers)
print(all_even)  # True

# いずれかの要素が条件を満たすかチェック
mixed = [1, 3, 5, 6, 7]
any_even = any(x % 2 == 0 for x in mixed)
print(any_even)  # True

カスタム高階関数の作成

1. 関数を引数として受け取る高階関数

def apply_operation(numbers, operation):
    return [operation(x) for x in numbers]

def square(x):
    return x ** 2

def cube(x):
    return x ** 3

numbers = [1, 2, 3, 4, 5]
squared = apply_operation(numbers, square)
cubed = apply_operation(numbers, cube)
print(f"2乗: {squared}")  # [1, 4, 9, 16, 25]
print(f"3乗: {cubed}")    # [1, 8, 27, 64, 125]

2. 関数を返す高階関数

def create_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier

# 2倍、3倍する関数を作成
double = create_multiplier(2)
triple = create_multiplier(3)

print(double(5))  # 10
print(triple(5))  # 15

3. クロージャの活用

def create_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

# カウンターを作成
counter1 = create_counter()
counter2 = create_counter()

print(counter1())  # 1
print(counter1())  # 2
print(counter2())  # 1(独立したカウンター)

デコレータ(高階関数の応用)

1. 基本的なデコレータ

def timer_decorator(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__}の実行時間: {end - start:.4f}秒")
        return result
    return wrapper

@timer_decorator
def slow_function():
    import time
    time.sleep(1)
    return "完了"

slow_function()  # 実行時間が表示される

2. 引数を持つデコレータ

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("太郎")  # 3回実行される

実践的な高階関数の応用

1. データ処理パイプライン

def pipeline(*functions):
    def apply(data):
        for func in functions:
            data = func(data)
        return data
    return apply

# データ処理関数
def add_one(x): return x + 1
def multiply_by_two(x): return x * 2
def square(x): return x ** 2

# パイプラインを作成
process = pipeline(add_one, multiply_by_two, square)
result = process(3)  # (3+1)*2の2乗 = 64
print(result)  # 64

2. 条件付きフィルタリング

def create_filter(condition):
    return lambda items: [item for item in items if condition(item)]

# 条件を定義
is_positive = lambda x: x > 0
is_even = lambda x: x % 2 == 0

numbers = [-2, -1, 0, 1, 2, 3, 4, 5]

# フィルターを作成
positive_filter = create_filter(is_positive)
even_filter = create_filter(is_even)

print(positive_filter(numbers))  # [1, 2, 3, 4, 5]
print(even_filter(numbers))      # [-2, 0, 2, 4]

3. 関数の合成

def compose(f, g):
    return lambda x: f(g(x))

def add_five(x):
    return x + 5

def multiply_by_three(x):
    return x * 3

# 関数を合成
composed = compose(add_five, multiply_by_three)
result = composed(2)  # (2 * 3) + 5 = 11
print(result)  # 11

関数型プログラミングのテクニック

1. 部分適用(Partial Application)

from functools import partial

def multiply(x, y):
    return x * y

# 部分適用で特定の倍数関数を作成
double = partial(multiply, 2)
triple = partial(multiply, 3)

print(double(5))  # 10
print(triple(5))  # 15

2. カリー化(Currying)

def curry_add(x):
    def add_y(y):
        def add_z(z):
            return x + y + z
        return add_z
    return add_y

# カリー化された関数の使用
add_1 = curry_add(1)
add_1_2 = add_1(2)
result = add_1_2(3)  # 1 + 2 + 3 = 6
print(result)  # 6

# 一行で実行
result2 = curry_add(10)(20)(30)  # 60
print(result2)  # 60

3. メモ化(Memoization)

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  # 高速計算

実用的な高階関数パターン

1. バリデーター関数

def create_validator(*validators):
    def validate(value):
        return all(validator(value) for validator in validators)
    return validate

# 個別のバリデーター
def min_length(length):
    return lambda s: len(s) >= length

def contains_digit():
    return lambda s: any(c.isdigit() for c in s)

# 複合バリデーター
password_validator = create_validator(
    min_length(8),
    contains_digit()
)

print(password_validator("password123"))  # True
print(password_validator("short"))        # False

2. イベントハンドラー

class EventManager:
    def __init__(self):
        self.handlers = []
    
    def add_handler(self, handler):
        self.handlers.append(handler)
    
    def trigger(self, event):
        for handler in self.handlers:
            handler(event)

# イベントハンドラーを定義
def log_handler(event):
    print(f"ログ: {event}")

def email_handler(event):
    print(f"メール送信: {event}")

# 使用例
manager = EventManager()
manager.add_handler(log_handler)
manager.add_handler(email_handler)
manager.trigger("ユーザー登録")

3. 設定可能な処理

def process_data(data, processors):
    for processor in processors:
        data = processor(data)
    return data

# 処理関数
def normalize(data):
    return [x.strip().lower() for x in data]

def remove_empty(data):
    return [x for x in data if x]

def add_prefix(prefix):
    return lambda data: [f"{prefix}{x}" for x in data]

# データ処理
raw_data = [" Hello ", "WORLD", "", "Python "]
processors = [normalize, remove_empty, add_prefix(">>> ")]
result = process_data(raw_data, processors)
print(result)  # ['>>> hello', '>>> world', '>>> python']

パフォーマンス最適化

1. ジェネレータとの組み合わせ

def filter_map(filter_func, map_func, iterable):
    for item in iterable:
        if filter_func(item):
            yield map_func(item)

# 大きなデータセットの効率的な処理
numbers = range(1000000)
result = filter_map(
    lambda x: x % 2 == 0,  # 偶数のみ
    lambda x: x ** 2,      # 2乗
    numbers
)

# 最初の5つだけ取得(メモリ効率的)
first_five = list(itertools.islice(result, 5))
print(first_five)  # [0, 4, 16, 36, 64]

2. 遅延評価

def lazy_operation(func):
    def wrapper(*args, **kwargs):
        def execute():
            return func(*args, **kwargs)
        return execute
    return wrapper

@lazy_operation
def expensive_calculation(n):
    return sum(i**2 for i in range(n))

# 関数は実行されない(遅延評価)
lazy_calc = expensive_calculation(10000)

# 必要な時に実行
result = lazy_calc()
print(result)

よくある間違いと対策

1. クロージャでの変数参照

# 間違い:すべて同じ値を参照
functions = []
for i in range(3):
    functions.append(lambda: i)  # すべて2を返す

# 正しい:デフォルト引数で値を固定
functions = []
for i in range(3):
    functions.append(lambda x=i: x)

for func in functions:
    print(func())  # 0, 1, 2

2. 副作用のある関数

# 避けるべき:副作用のある高階関数
def bad_map(func, lst):
    for i in range(len(lst)):
        lst[i] = func(lst[i])  # 元のリストを変更
    return lst

# 推奨:純粋関数
def good_map(func, lst):
    return [func(x) for x in lst]  # 新しいリストを返す

まとめ

Python高階関数をマスターすることで、より洗練されたコードが書けるようになります。map、filter、reduceなどの基本的な高階関数から、デコレータ、クロージャ、関数合成まで、幅広いテクニックを活用することで、コードの再利用性と可読性が大幅に向上します。

特に重要なのは、関数型プログラミングの思考法を身につけることです。副作用を避け、純粋関数を意識することで、バグの少ない保守しやすいコードが作成できます。

実際のプロジェクトでは、適切な場面で高階関数を使い分け、過度に複雑にならないよう注意しながら、Pythonの強力な機能を最大限に活用してください。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座