💡

【Python】ラッパー関数とは何か?

に公開

今日は「ラッパー関数」について楽しく学んでいきましょう。

なんだか難しそうな名前ですが、実はとても便利な関数です。

ラッパー関数って何?

ラッパー関数とは、すでにある関数を「包む(ラップする)」新しい関数のこと。

包み紙でプレゼントを包むように、元の関数を新しい関数で包んでしまうんです。

「なぜそんなことをするの?」と思いますよね。

これには以下のようなメリットがあります:

  1. 元の関数に新しい機能を追加できる
  2. 元の関数の使い方を簡単にできる
  3. 元の関数を変更せずに動作を変えられる

ラッパー関数の簡単な例

まずは簡単な例で見てみましょう。

こちらは「挨拶をする関数」とそのラッパー関数です:

# 元の関数
def say_hello(name):
    return f"こんにちは、{name}さん!"

# ラッパー関数
def friendly_hello(name):
    # 元の関数を呼び出して、結果を少し変更する
    original_greeting = say_hello(name)
    return original_greeting + " 素敵な一日をお過ごしください!"

# 使ってみる
print(say_hello("太郎"))     # 出力: こんにちは、太郎さん!
print(friendly_hello("花子")) # 出力: こんにちは、花子さん! 素敵な一日をお過ごしください!

見てください!

friendly_hello関数はsay_hello関数をラップして、挨拶にちょっとした追加メッセージをつけています。

元の関数はそのままで、新しい機能を追加できました。

もう少し実用的な例:処理時間を計測するラッパー関数

次は、関数の実行時間を測定するラッパー関数の例です:

import time

# 時間のかかる処理をする関数(例として)
def heavy_calculation(n):
    # 時間のかかる計算の例
    total = 0
    for i in range(n):
        total += i
    return total

# 処理時間を計測するラッパー関数
def measure_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 開始時間
        result = func(*args, **kwargs)  # 元の関数を実行
        end_time = time.time()  # 終了時間
        print(f"処理時間: {end_time - start_time:.4f}秒")
        return result
    return wrapper

# ラッパー関数を使う
timed_calculation = measure_time(heavy_calculation)

# 実行してみる
result = timed_calculation(1000000)
print(f"計算結果: {result}")

この例では、measure_timeがラッパー関数になっていて、どんな関数でも処理時間を測定できるようになりました。

heavy_calculation関数自体は変更せずに、新しい機能(時間計測)を追加できたんです!

デコレータ:Pythonのラッパー関数の特別な書き方

Pythonでは、「デコレータ」という特別な書き方でラッパー関数を使うことができます。

デコレータを使うと、もっとスッキリとコードが書けるんですよ。

import time

# デコレータとしてのラッパー関数
def measure_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"処理時間: {end_time - start_time:.4f}秒")
        return result
    return wrapper

# デコレータを使って関数を定義
@measure_time
def heavy_calculation(n):
    total = 0
    for i in range(n):
        total += i
    return total

# 直接実行できる
result = heavy_calculation(1000000)
print(f"計算結果: {result}")

@measure_timeという書き方で、簡単にheavy_calculation関数に時間計測機能を追加できました。これがPythonのデコレータです!

ラッパー関数の実用例

ラッパー関数は実際のプログラミングでもよく使われています。いくつかの実用例を見てみましょう:

1. ログ出力を追加する

def add_logging(func):
    def wrapper(*args, **kwargs):
        print(f"関数 {func.__name__} が呼び出されました")
        result = func(*args, **kwargs)
        print(f"関数 {func.__name__} が終了しました")
        return result
    return wrapper

@add_logging
def greet(name):
    return f"やあ!{name}さん"

message = greet("次郎")
print(message)

2. エラー処理を一括で行う

def handle_errors(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"エラーが発生しました: {e}")
            return None
    return wrapper

@handle_errors
def divide(a, b):
    return a / b

result1 = divide(10, 2)  # 正常に動作
print(f"結果1: {result1}")

result2 = divide(10, 0)  # エラーが発生するけど、プログラムは停止しない
print(f"結果2: {result2}")

ラッパー関数のメリットまとめ

ラッパー関数を使うことで得られるメリットをまとめてみましょう:

メリット 説明
コードの再利用 同じ機能を複数の関数に適用できる
関心の分離 機能ごとに処理を分けられる
コードの可読性 元の関数の目的がわかりやすくなる
拡張性 元のコードを変更せずに機能追加できる
デバッグのしやすさ ログ出力などを簡単に追加できる

まとめ:ラッパー関数はプログラミングの便利ツール!

ラッパー関数は、既存の関数に新しい機能を追加したり、使い方を簡単にしたりするための強力なテクニックです。

特にPythonのデコレータを使うと、とても簡潔にラッパー関数を実装できます。

デコレータについては、以下の記事でもまとめています。

https://zenn.dev/hovinci/articles/72f55ed12a67c7

プログラミングは「車輪の再発明」(同じものを何度も作ること)を避けるべきと言われますが、ラッパー関数はその考え方にぴったり。

すでにある関数をそのまま活用しながら、新しい機能を追加できます。

Discussion