{}const=>[]async()letfn</>var
開発Python

Pythonのデコレーター:コードを変更せずに機能を拡張する

Pythonのデコレーターを使用して、機能をエレガントに拡張する方法を学びます。 ロギング、キャッシュ、実行時間の測定、アクセス権の確認などの実例を分析します。初心者と経験豊富な開発者向けのコード例を使用したわかりやすい説明。

К

Kodik

著者

2分で読める

Webアプリケーションを開発していて、すべてのAPI呼び出しをログに記録し、ユーザーのアクセス権を確認し、関数の実行時間を測定する必要があるとします。もちろん、同じコードを各関数にコピーすることもできますが、よりエレガントな方法があります。それはデコレーターです。これは、Pythonの最も強力なツールの1つであり、ソースコードを変更せずに関数の動作を変更できます。

デコレーターとは

デコレーターは、別の関数を引数として受け取り、拡張された機能を持つ新しい関数を返す関数です。難しそうですか?実際には、思ったよりも簡単です。

def my_decorator(func):
    def wrapper():
        print("Something happens before the function is called")
        func()
        print("Something happens after calling the function")
    return wrapper

@my_decorator
def say_hello():
    print("Hi!")

say_hello()

実行結果:

Что-то происходит до вызова функции
Привет!
Что-то происходит после вызова функции

シンボル@はPythonの構文糖です。関数の前の@my_decoratorのエントリは、say_hello = my_decorator(say_hello)と同等です。

🔥 10万人以上の学生が参加中

理論を読むのに疲れた?
コーディングの時間だ!

Kodik — 実践でプログラミングを学ぶアプリ。AIメンター、インタラクティブなレッスン、実際のプロジェクト。

🤖 AI 24時間
🎓 修了証
💰 無料
🚀 始める
今日参加

引数付きデコレーター

しかし、関数がパラメータを受け取った場合はどうすればよいですか?これを行うには、*args**kwargsを使用します。

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(times=3)
def greet(name):
    print(f"Hi, {name}!")

greet("Alexey")

結論:

Привет, Алексей!
Привет, Алексей!
Привет, Алексей!

ここでは、関数の呼び出しを指定された回数繰り返すパラメータを持つデコレーターを作成しました。

デコレーターの実例

実行時間の測定

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"The {func.__name__} function was executed in {end - start:.4f} seconds")
        return result
    return wrapper

@timer
def heavy_computation():
    time.sleep(2)
    return "Done"

heavy_computation()

@wraps(func) に注意してください。これは、元の関数のメタデータ(名前、ドキュメントなど)を保持する、モジュール functools からの組み込みデコレーターです。

結果のキャッシュ

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

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

print(fibonacci(100))  # キャッシュにより即座に実行されます

アクセス権の確認

def require_auth(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.get('is_authenticated'):
            raise PermissionError("Authorization required")
        return func(user, *args, **kwargs)
    return wrapper

@require_auth
def delete_account(user, account_id):
    print(f"Account {account_id} has been deleted")

user = {'is_authenticated': True, 'username': 'admin'}
delete_account(user, 123)

デコレーターのチェーン

デコレーターは、複数の関数を1つの関数に適用して組み合わせることができます。

@timer
@memoize
def complex_calculation(x, y):
    time.sleep(1)
    return x ** y

# 最初の呼び出しには約1秒かかります
complex_calculation(2, 10)

# 2回目の呼び出しは瞬時に行われます(結果はキャッシュされます)
complex_calculation(2, 10)

デコレーターは下から上に適用されます。つまり、最初に memoize、次に timer です。

デコレーターとしてのクラス

デコレーターは必ずしも関数である必要はありません。クラスを使用することができます。

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Call #{self.count} of the {self.func.__name__} function"        return self.func(*args, **kwargs)

@CountCalls
def process_data():
    print("Processing data...")

process_data()
process_data()
process_data()

Pythonの組み込みデコレーター

Python には、いくつかの便利な組み込みデコレーターが用意されています。

  • @property — メソッドをクラス属性に変換します

  • @staticmethod — 静的メソッドを作成します

  • @classmethod — クラスメソッドを作成します

  • @functools.lru_cache —サイズ制限付きキャッシュ

  • @dataclass — クラスメソッドの自動生成

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(param):
    # 複雑な計算
    return param * 2

デコレーターを使用するタイミング

デコレーターは、ログ、入力データの検証、例外処理、アクセス制御、キャッシュ、パフォーマンス測定、ネットワークエラーの再試行ロジックなど、多くの関数に適用する必要があるタスクに最適です。デコレーターは、DRY(Don't Repeat Yourself)の原則を守り、コードをよりクリーンで明確にするのに役立ちます。

潜在的な問題

デコレーターの力にもかかわらず、いくつかのポイントを覚えておく必要があります。デコレーターを過剰に使用すると、コードのデバッグが難しくなる可能性があります。デコレーターは、生産性にわずかなオーバーヘッドを追加します。関数のメタデータを保存するには、常に @wraps を使用することが重要です。多くのデコレーターのチェーンは、コードの読みやすさを低下させる可能性があります。

履歴書

デコレーターは、コードを変更せずにコードの機能を変更および拡張するための優雅な方法です。これらは、Flask や Django などの一般的なフレームワークで広く使用されており、その仕組みを理解することで、開発者の作業が大幅に簡素化されます。ログや時間測定のためのシンプルなデコレーターから始めると、徐々にそれらが役立つ状況が増えていきます。

デコレーターやその他の高度なPython機能を実際に習得したいですか?

アプリケーション コディック ロシア語でのわかりやすい説明と実際の実践例を使用して、快適なペースでプログラミングを学べるように特別に作成されています。私たちは、基礎から専門的なテクニックまで、複雑なトピックをシンプルな言葉で分析します。各レッスンは、構文を単に暗記するだけでなく、実際のプロジェクトで知識を適用する方法を理解できるように構成されています。

また、トレーニング中に質問が生じた場合は、当社の テレグラムチャンネル!これは、どんな質問でも尋ねて詳細な回答を得ることができる、活気のある開発者コミュニティです。私たちの雰囲気はフレンドリーで有益です。毎日、私たちは開発のトップテーマを分析し、経験を共有し、お互いの成長を助け合っています。Kodikに参加して、効果的で面白い学習方法を本当に理解している人たちと一緒にプログラミングの旅を始めましょう!

🎯先延ばしをやめよう

記事は気に入った?
実践の時間だ!

Kodikでは読むだけでなく、すぐにコードを書く。理論 + 実践 = 本当のスキル。

即座に実践
🧠AIがコードを説明
🏆修了証

登録不要 • カード不要