検索

キーワード


目次

Pythonにおけるデコレータについて【初心者向け解説記事】

  • 公開日:2023-07-20 20:24:18
  • 最終更新日:2023-07-20 20:24:08
Pythonにおけるデコレータについて【初心者向け解説記事】

Workteria(ワークテリア)では難易度の高いものから低いものまで、スキルや経験に合わせた案件を多数揃えています。会員登録は無料ですので、ぜひ会員登録してご希望の案件を探してみてください!

フリーランス/正社員のエンジニアとして活躍するには、ご自身のスキルや経験に合わせた仕事を選ぶことが大切です。ご希望の案件がみつからない場合はお気軽にお問い合わせください!ユーザ満足度の高いキャリアコンサルタントが在籍していますので、希望条件や悩み事などなんでもご相談ください。ご希望にピッタリの案件をご紹介させていただきます。

Pythonにおけるデコレータとは?

デコレータは、Pythonの強力な機能であり、関数やメソッド、クラスの振る舞いを変更するために使用されます。デコレータを使用することで、既存のコードを修正することなく新たな機能を追加することが可能になります。それでは、デコレータがどのように動作するのか詳しく見ていきましょう。


デコレータの基本

Pythonのデコレータは、他の関数を引数として受け取り、新しい関数を返す関数です。ここに、単純なデコレータの例を示します。

def simple_decorator(function):
    def wrapper():
        print("関数実行開始")
        function()
        print("関数実行終了")
    return wrapper

@simple_decorator
def greet():
    print("Hello, world!")

greet()

これを実行すると、以下のように出力されます。

関数実行開始
Hello, world!
関数実行終了

上記のコードでは、simple_decoratorがデコレータとして機能しています。このデコレータは、関数の実行前後にメッセージを出力します。デコレータを適用するには、@記号を使用します。


デコレータの具体的な使用例

以下に、実際の開発作業でよく利用されるデコレータの使用例をいくつか示します。

ログの記録

デコレータを使用して、関数の呼び出しやその結果をログに記録することができます。これはデバッグや監視に有用です。

import logging

# ログの基本設定を行う。ログレベルをINFOに設定。
logging.basicConfig(level=logging.INFO)

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f'{func.__name__} called with {args}, {kwargs}')
        result = func(*args, **kwargs)
        logging.info(f'{func.__name__} returned {result}')
        return result
    return wrapper

@log_decorator
def add(x, y):
    return x + y

result = add(1, 2)
print(result)

これを実行すると、コンソールに以下のように出力されます。

3
INFO:root:add called with (1, 2), {}
INFO:root:add returned 3


関数の実行時間の計測

関数の実行時間を計測し、その時間を出力またはログに記録するデコレータも作成することができます。これはパフォーマンス分析や最適化に役立ちます。

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time() # 開始時間
        result = func(*args, **kwargs)
        end_time = time.time() # 終了時間
        print(f'{func.__name__} の実行時間: {end_time - start_time} 秒')
        return result
    return wrapper

@timing_decorator
def slow_add(x, y):
    time.sleep(2) # 2秒間処理を停止する
    return x + y

result = slow_add(1, 2) 

出力は以下のようになります。

slow_add の実行時間: 2.0159220695495605 秒


エラーハンドリング

特定のエラーを捕捉し、そのエラーに対する処理をデコレータ内で行うことも可能です。このようにして、エラーハンドリングのコードを一箇所にまとめることができます。

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

@error_handling_decorator
def unsafe_division(x, y):
    return x / y

result = unsafe_division(1, 0)

このコードを実行すると、以下のように出力されます。

エラーが発生しました: division by zero


リトライロジックの適用

失敗した関数の再試行をデコレータで実装することができます。主にネットワーク接続に失敗した際の再接続の試行などに使用されます。

import time
import random

def retry_decorator(max_retries=3, delay=1):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(max_retries):
                try:
                    return func(*args, **kwargs)
                # 例外が発生した場合は、delay秒待ってから再試行する
                except Exception as e:
                    # max_retries回試行した場合は例外を発生させる
                    if i < max_retries - 1:
                        print(f'{func.__name__}{e}により実行できませんでした。{delay}秒後に再試行します。')
                        time.sleep(delay)
                    else:
                        print(f'{func.__name__}{e}により実行できませんでした。{max_retries}回の試行に失敗しました。')
                        raise
        return wrapper
    return decorator

@retry_decorator(max_retries=5, delay=2)
def unreliable_function():
    # 50%の確率でValueErrorを発生させる
    if random.random() < 0.5:
        raise ValueError('ValueError')
    else:
        return '成功しました。'

result = unreliable_function()
print(result)

このコードを出力すると、一例として以下のように出力されます。乱数によって結果が変わるため、実行するたびに出力内容は変わります。

unreliable_functionがValueErrorにより実行できませんでした。2秒後に再試行します。
unreliable_functionがValueErrorにより実行できませんでした。2秒後に再試行します。
unreliable_functionがValueErrorにより実行できませんでした。2秒後に再試行します。
成功しました。


デコレータの構造と振る舞い

引数を持つ関数のデコレータ

引数を持つ関数をデコレートする場合、デコレータの中に引数を渡すためのラッパー関数を追加する必要があります。

def decorator_with_args(function):
    def wrapper(*args, **kwargs):
        print("関数実行開始")
        function(*args, **kwargs)
        print("関数実行終了")
    return wrapper

@decorator_with_args
def greet(name):
    print(f"こんにちは, {name}さん!")

greet("鈴木") #こんにちは、鈴木さん!


クラスを修飾するデコレータ

Pythonでは、クラスもデコレートできます。クラスをデコレートすると、クラスの振る舞い全体を変更することが可能になります。具体的な例を見てみましょう。

def class_decorator(cls):
    class NewClass(cls):
        def __init__(self, *args, **kwargs):
            print("インスタンスを作成します")
            super().__init__(*args, **kwargs)
            print("インスタンスを作成しました")
    return NewClass

@class_decorator
class MyClass:
    def __init__(self, value):
        self.value = value

my_instance = MyClass(10)

この例では、class_decoratorは新しいクラスを作成し、元のクラスの振る舞いを継承しつつ、新たな処理(ここではインスタンス作成の開始と終了を表示する)を追加しています。


デコレータの順番とその影響

複数のデコレータを一つの関数に対して適用する場合、デコレータの適用順序は重要です。デコレータは上から順に適用され、それぞれのデコレータが前のデコレータの結果に基づいて実行されます。具体的な例を見てみましょう。

def decorator1(func):
    def wrapper():
        print("デコレータ1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("デコレータ2")
        func()
    return wrapper

@decorator1
@decorator2
def greet():
    print("Hello, world!")

greet()


この例では、decorator1decorator2の結果に対して適用されます。したがって、"デコレータ1"のメッセージが"デコレータ2"よりも先に出力されます。このように、デコレータの適用順序は結果に大きな影響を与えます。


パラメータ付きデコレータ

デコレータ自体にパラメータを指定することもできます。この場合、パラメータを受け取る関数をデコレータ関数の上位に置きます

def decorator_with_parameters(param1, param2):
    def decorator(function):
        def wrapper(*args, **kwargs):
            print(f"デコレータのパラメータ: パラメータ1:{param1}, パラメータ2:{param2}")
            function(*args, **kwargs)
        return wrapper
    return decorator

@decorator_with_parameters("Hello", "World")
def greet():
    pass

greet()

出力は以下のようになります。

デコレータのパラメータ: パラメータ1:Hello, パラメータ2:World


まとめ

Pythonのデコレータは、関数やクラスの振る舞いを変更するための強力なツールです。デコレータを使用することで、既存のコードを修正せずに新たな機能を追加したり、コードの再利用性を向上させることができます。しかし、デコレータは高度な概念であるため、その使用は注意が必要です。特に、デコレータの適用順序や、引数を持つデコレータの作成には注意が必要です。以上を踏まえて、デコレータの使用を検討してみてください。


【著者】

ゆうさい

フォワードソフト株式会社のエンジニア。Java、Python、JavaScript、C#などの言語の他、クラウドやネットワーク技術を勉強しています。PythonやVBAを使った自動化で楽をする方法を考えるのが好きです。 最近はジェネレーティブAIの業務利用に関する検証を行っています。 資格を通じて知識を吸収することを心がけており、セキュリティスペシャリスト、データベーススペシャリスト、応用情報技術者、Oracle Certified Java Programmer Gold SE 11、Pythin3 エンジニア認定試験、HTML5プロフェッショナル認定試験レベル2、AWSプラクティショナーなどの情報資格を保有しています。

編集した記事一覧

正社員/フリーランスの方でこのようなお悩みありませんか?

  • 自分に合う案件を定期的に紹介してもらいたい
  • 週2、リモートワークなど自由な働き方をしてみたい
  • 面倒な案件探し・契約周りは任せて仕事に集中したい

そのような方はぜひ、Workteriaサイトをご利用ください!

  • 定期的にご本人に合う高額案件を紹介

  • リモートワークなど自由な働き方ができる案件多数

  • 専属エージェントが契約や請求をトータルサポート