Python デコレータ: 包括的なガイド
私が Python でプログラミングを始めたとき、確かバージョンは 3.3 でした。したがって、私がプログラミングを始めたとき、デコレーターは長い間 Python コミュニティで利用可能でした。
関数デコレータはバージョン 2.2 で Python に導入され、クラス デコレータはバージョン 2.6 で Python に導入されました。
個人的には、Python の Decorator 機能はこの言語の非常に強力な機能であると考えています。
実際、私の目標は、Python で最も理解しにくいトピックについて一連の記事を作成することです。 10 個以上あるこれらのトピックを 1 つずつ取り上げる予定です。
この記事では、デコレータのトピックのあらゆる部分についてできる限り触れていきます。
1. 歴史的背景
- 初期 (Python 2.2 以前): デコレーターが登場する前は、関数やクラスの変更には手動のラッピングやモンキーパッチが必要になることが多く、煩雑で読みにくくなっていました。
- メタクラス (Python 2.2): メタクラスはクラスの作成を制御する方法を提供し、後にデコレーターが提供する機能の一部を提供しますが、単純な変更には複雑でした。
- PEP 318 (Python 2.4): デコレーターは、PEP 318 を通じて Python 2.4 に正式に導入されました。この提案は Java のアノテーションからインスピレーションを得たもので、関数やメソッドを変更するためのよりクリーンでより宣言的な方法を提供することを目的としています。 .
- クラス デコレーター (Python 2.6): Python 2.6 では、デコレーターのサポートがクラスに拡張され、クラスの汎用性がさらに強化されました。
- 広範な採用: デコレータはすぐに人気の機能となり、Flask や Django などのフレームワークでルーティングや認証などに広く使用されています。
2. デコレータとは何ですか?
本質的に、デコレータは Python の設計パターンであり、これを使用すると、コア構造を変更せずに関数またはクラスの動作を変更できます。デコレーターはメタプログラミングの一種であり、基本的に他のコードを操作するコードを作成します。
Python は、以下の順序で指定されたスコープを使用して名前を解決することをご存知でしょう:
- ローカル
- 同封
- グローバル
- 内蔵
デコレータは、クロージャの概念と密接に関連するエンクロージング スコープに座っています。
重要なアイデア: デコレーターは関数を入力として受け取り、それに機能を追加し、変更された関数を返します。
例え: デコレーターをギフトの包装紙と考えてください。ギフト (本来の機能) があり、それを装飾紙 (デコレーター) で包み、見た目を良くしたり、追加の機能 (リボンやカードなど) を追加したりします。中身のギフトは変わりませんが、そのプレゼンテーションや関連するアクションが強化されています。
A) デコレータのバリエーション: 関数ベースとクラスベース
Python のほとんどのデコレータは関数を使用して実装されますが、クラスを使用してデコレータを作成することもできます。
関数ベースのデコレータはより一般的でシンプルですが、クラスベースのデコレータはさらなる柔軟性を提供します。
関数ベースの基本的なデコレータ構文
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before calling the decorated function print("Before function call") result = func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result return wrapper @my_decorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
説明:
- my_decorator はデコレータ関数です。装飾される関数 func を入力として受け取ります。
- Wrapper は、元の関数の呼び出しをラップする内部関数です。元の関数の前後でコードを実行できます。
- @my_decorator はデコレータ構文です。これは、say_hello = my_decorator(say_hello) と同等です。
クラスベースの基本的なデコレータ構文
これらは、関数の代わりにクラスを使用してデコレーターを定義します。
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # Do something before calling the decorated function print("Before function call") result = self.func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
説明:
- MyDecorator は、デコレーターとして機能するクラスです。
- __init__ メソッドには、装飾される関数が格納されます。
- __call__ メソッドはクラス インスタンスを呼び出し可能にし、関数のように使用できるようにします。
B) 単純なデコレータの実装
デコレーターの基本概念は、別の関数を引数として受け取り、明示的に変更することなくその動作を拡張する関数であるということです。
最も単純な形式は次のとおりです:
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper # Using the decorator with @ syntax @my_decorator def say_hello(): print("Hello!") # When we call say_hello() say_hello() # This is equivalent to: # say_hello = my_decorator(say_hello)
C) 引数を使用したデコレータの実装
関数の実行時間を記録するデコレータを作成しましょう:
def decorator_with_args(func): def wrapper(*args, **kwargs): # Accept any number of arguments print(f"Arguments received: {args}, {kwargs}") return func(*args, **kwargs) # Pass arguments to the original function return wrapper @decorator_with_args def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") greet("Alice", greeting="Hi") # Prints arguments then "Hi, Alice!"
D) パラメータ化されたデコレータの実装
これらは独自のパラメータを受け入れることができるデコレータです:
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"Hello {name}") return "Done" greet("Bob") # Prints "Hello Bob" three times
E) クラスデコレータの実装
def singleton(cls): instances = {} def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class DatabaseConnection: def __init__(self): print("Initializing database connection") # Creating multiple instances actually returns the same instance db1 = DatabaseConnection() # Prints initialization db2 = DatabaseConnection() # No initialization printed print(db1 is db2) # True
F) メソッドデコレータの実装
これらは、クラス メソッド用に特別に設計されています:
def debug_method(func): def wrapper(self, *args, **kwargs): print(f"Calling method {func.__name__} of {self.__class__.__name__}") return func(self, *args, **kwargs) return wrapper class MyClass: @debug_method def my_method(self, x, y): return x + y obj = MyClass() print(obj.my_method(5, 3))
G) デコレータチェーンの実装
複数のデコレータを 1 つの関数に適用できます:
def bold(func): def wrapper(): return "<b>" + func() + "</b>" return wrapper def italic(func): def wrapper(): return "<i>" + func() + "</i>" return wrapper @bold @italic def greet(): return "Hello!" print(greet()) # Outputs: <b><i>Hello!</i></b>
説明:
- デコレータは下から上に適用されます。
- これは、数学で行うこと、f(g(x)) に似ています。
- 斜体が最初に適用され、次に太字が適用されます。
H) @functools.wraps を使用しない場合はどうなりますか?
functools.wraps デコレータ (ドキュメントを参照) は、デコレータでラップしたときに元の関数のメタデータ (名前、docstring、シグネチャなど) を保持するヘルパー関数です。使用しないと、この重要な情報が失われます。
例:
def my_decorator(func): def wrapper(*args, **kwargs): """Wrapper docstring""" return func(*args, **kwargs) return wrapper @my_decorator def my_function(): """My function docstring""" pass print(my_function.__name__) print(my_function.__doc__)
出力:
wrapper Wrapper docstring
問題:
- 元の関数の名前 (my_function) と docstring ("My function docstring") は失われます。
- これにより、デバッグとイントロスペクションが困難になる可能性があります。
解決策: functools.wraps を使用します):
def my_decorator(func): def wrapper(*args, **kwargs): # Do something before calling the decorated function print("Before function call") result = func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result return wrapper @my_decorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
出力:
class MyDecorator: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # Do something before calling the decorated function print("Before function call") result = self.func(*args, **kwargs) # Do something after calling the decorated function print("After function call") return result @MyDecorator def say_hello(name): print(f"Hello, {name}!") say_hello("World")
functools.wraps の利点:
- 関数のメタデータを保持します。
- コードの可読性と保守性が向上します。
- デバッグが容易になります。
- イントロスペクション ツールとドキュメント ジェネレーターに役立ちます。
I) 状態を持つデコレータ
デコレータは関数呼び出し間の状態を維持することもできます。これは、関数呼び出しのキャッシュやカウントなどのシナリオで特に役立ちます。
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper # Using the decorator with @ syntax @my_decorator def say_hello(): print("Hello!") # When we call say_hello() say_hello() # This is equivalent to: # say_hello = my_decorator(say_hello)
出力:
def decorator_with_args(func): def wrapper(*args, **kwargs): # Accept any number of arguments print(f"Arguments received: {args}, {kwargs}") return func(*args, **kwargs) # Pass arguments to the original function return wrapper @decorator_with_args def greet(name, greeting="Hello"): print(f"{greeting}, {name}!") greet("Alice", greeting="Hi") # Prints arguments then "Hi, Alice!"
説明:
ラッパー関数は、装飾された関数が呼び出されるたびに増加するカウンター (呼び出し) を維持します。
これは、デコレータを使用して状態を維持する方法の簡単な例です。
J) Python デコレータのベスト プラクティス
- functools.wraps を使用する: 元の関数のメタデータを保持するには、デコレーターで常に @functools.wraps を使用します。
- デコレータをシンプルに保つ: デコレータは、理想的には 1 つの特定のことを、それをうまく実行する必要があります。これにより、再利用可能になり、理解しやすくなります。
- デコレータを文書化します: デコレータが何をするのか、どのような引数を受け取り、何を返すのかを説明します。
- デコレータをテストする: 単体テストを作成して、さまざまなシナリオでデコレータが期待どおりに動作することを確認します。
- チェーンの順序を考慮する: 複数のデコレータをチェーンするときは、実行フローに影響するため、順序に注意してください。
K) 不適切な実装 (アンチパターン)
- 過度に複雑なデコレータ: 複雑すぎるデコレータを作成したり、多くのことを実行しようとしたりするデコレータを作成しないでください。そのため、理解、保守、デバッグが困難になります。
- functools.wraps の無視: @functools.wraps の使用を忘れると、関数のメタデータが失われ、イントロスペクションやデバッグで問題が発生する可能性があります。
- 副作用: デコレーターには、装飾された関数の変更以外に意図しない副作用が発生しないことが理想的です。
- 値のハードコーディング: デコレーター内の値のハードコーディングは避けてください。代わりに、デコレータ ファクトリを使用して設定可能にします。
- 引数が適切に処理されない: デコレータがさまざまな関数で使用されることを意図している場合は、ラッパー関数が *args と **kwargs を使用して任意の数の位置引数とキーワード引数を処理できることを確認してください。
L) 10. 現実世界の使用例
- ログ: デバッグまたは監査のために関数呼び出し、引数、戻り値を記録します。
- タイミング: パフォーマンス分析のための関数の実行時間を測定します。
- キャッシュ: 冗長な計算 (メモ化) を避けるために、高コストの関数呼び出しの結果を保存します。
- 認証と認可: 関数を実行する前にユーザーの資格情報または権限を確認します。
- 入力検証: 関数に渡された引数が特定の基準を満たしているかどうかを確認します。
- レート制限: 特定の期間内に関数を呼び出すことができる回数を制御します。
- 再試行ロジック: 一時的なエラーにより関数呼び出しが失敗した場合、関数呼び出しを自動的に再試行します。
- フレームワーク固有のタスク: Flask や Django などのフレームワークは、ルーティング (URL を関数にマッピング)、プラグインの登録などにデコレーターを使用します。
M) Python デコレータの厳選されたリスト
Python デコレータの厳選されたリストは以下で見つけることができます:
- 素晴らしい Python デコレータ
- Python デコレータ ライブラリ
N) 11. 結論
デコレーターは、Python の強力かつエレガントな機能であり、関数やクラスをクリーンで宣言的な方法で強化できます。
原則、ベスト プラクティス、潜在的な落とし穴を理解することで、デコレータを効果的に活用して、よりモジュール化され、保守しやすく、表現力豊かなコードを作成できます。
これらは、Python プログラマーにとって貴重なツールであり、特にフレームワークを使用したり、再利用可能なコンポーネントを構築したりする場合に役立ちます。
以上がPython デコレータ: 包括的なガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









LinuxターミナルでPythonバージョンを表示する際の許可の問題の解決策PythonターミナルでPythonバージョンを表示しようとするとき、Pythonを入力してください...

PythonのPandasライブラリを使用する場合、異なる構造を持つ2つのデータフレーム間で列全体をコピーする方法は一般的な問題です。 2つのデータがあるとします...

この記事では、numpy、pandas、matplotlib、scikit-learn、tensorflow、django、flask、and requestsなどの人気のあるPythonライブラリについて説明し、科学的コンピューティング、データ分析、視覚化、機械学習、Web開発、Hの使用について説明します。

UvicornはどのようにしてHTTPリクエストを継続的に聞きますか? Uvicornは、ASGIに基づく軽量のWebサーバーです。そのコア機能の1つは、HTTPリクエストを聞いて続行することです...

Pythonでは、文字列を介してオブジェクトを動的に作成し、そのメソッドを呼び出す方法は?これは一般的なプログラミング要件です。特に構成または実行する必要がある場合は...

10時間以内にコンピューター初心者プログラミングの基本を教える方法は?コンピューター初心者にプログラミングの知識を教えるのに10時間しかない場合、何を教えることを選びますか...

正規表現は、プログラミングにおけるパターンマッチングとテキスト操作のための強力なツールであり、さまざまなアプリケーションにわたるテキスト処理の効率を高めます。
