ホームページ > バックエンド開発 > Python チュートリアル > Python デコレータを 1 つの記事で理解する

Python デコレータを 1 つの記事で理解する

WBOY
リリース: 2023-04-12 21:40:13
転載
1093 人が閲覧しました

Python デコレータを 1 つの記事で理解する

Python は初心者に優しい言語です。ただし、デコレータなど、使いこなすのが難しい高度な機能も数多くあります。多くの初心者は、デコレータとその仕組みを理解したことがありません。この記事では、デコレータの詳細を紹介します。

Python では、関数は非常に柔軟な構造であり、変数に代入したり、パラメータとして別の関数に渡したり、関数の出力として使用したりできます。デコレーターは本質的に、他の関数が変更せずに機能を追加できるようにする関数です。

これは「装飾」の意味です。この「装飾」自体が機能を表します。別の機能を変更するために使用される場合、その機能をそれらの機能に追加します。

一般的に、デコレータが提供する @ 構文シュガー (Syntactic Sugar) を使用して、他の関数やオブジェクトを装飾できます。以下に示すように、@dec デコレータを使用して関数 func () を装飾します:

@dec
def func():
 pass
ログイン後にコピー

デコレータを理解する最良の方法は、デコレータがどのような問題を解決するかを理解することです。この記事では、デコレータを段階的に紹介します。特定の問題から出発し、その優雅さと力を誇示します。

問題の設定

デコレータの目的を理解するために、簡単な例を見てみましょう。 2 番目のパラメーターのデフォルト値が 10 である単純な加算関数 dec.py があるとします。

# dec.py
def add(x, y=10):
 return x + y
ログイン後にコピー

この加算関数を詳しく見てみましょう:

>>> add(10, 20)
30
>>> add
<function add at 0x7fce0da2fe18>
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>> add.__defaults__ # default value of the `add` function
(10,)
>>> add.__code__.co_varnames # the variable names of the `add` function
('x', 'y')
ログイン後にコピー

必要はありません。これらが何であるかを理解するには、Python のすべての関数がオブジェクトであり、さまざまなプロパティとメソッドがあることを覚えておいてください。また、検査モジュールを使用して add() 関数のソース コードを表示することもできます:

>>> from inspect import getsource
>>> print(getsource(add))
def add(x, y=10):
 return x + y
ログイン後にコピー

ここで、何らかの方法で加算関数を使用します。たとえば、いくつかの操作を使用して関数をテストします:

# dec.py
from time import time
def add(x, y=10):
 return x + y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
Output: i
add(10) 20
add(20, 30) 50
add("a", "b") ab
ログイン後にコピー

各操作の時間を知りたい場合は、time モジュールを呼び出すことができます:

# dec.py
from time import time
def add(x, y=10):
 return x + y
before = time()
print('add(10)', add(10))
after = time()
print('time taken: ', after - before)
before = time()
print('add(20, 30)', add(20, 30))
after = time()
print('time taken: ', after - before)
before = time()
print('add("a", "b")', add("a", "b"))
after = time()
print('time taken: ', after - before)
Output:
add(10) 20
time taken:6.699562072753906e-05
add(20, 30) 50
time taken:6.9141387939453125e-06
add("a", "b") ab
time taken:6.9141387939453125e-06
ログイン後にコピー

さて、プログラマとして、少しかゆいところはありませんか? 結局のところ、私たちはコピーしたくないのです。常に同じコードを貼り付けます。現在のコードはあまり読みにくくなっています。何かを変更したい場合は、表示されている箇所をすべて変更する必要があります。Python にはもっと良い方法があるはずです。

次のように、追加関数で実行時間を直接取得できます。

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
ログイン後にコピー

この方法は、前の方法よりも明らかに優れています。しかし、別の機能がある場合、これは不便に思われます。複数の関数がある場合:

# dec.py
from time import time
def add(x, y=10):
 before = time()
 rv = x + y
 after = time()
 print('time taken: ', after - before)
 return rv
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
ログイン後にコピー

add と sub は両方とも関数であるため、これを利用してタイマー関数を作成できます。関数の操作時間を計算するタイマーが必要です:

def timer(func, x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
ログイン後にコピー

これは便利ですが、次のようにタイマー関数を使用してさまざまな関数をラップする必要があります:

print('add(10)', timer(add,10)))
ログイン後にコピー

デフォルト値はまだ 10 ですか?必ずしも。では、どうすればもっと良くなるでしょうか?

ここにアイデアがあります: 新しいタイマー関数を作成し、他の関数をラップし、ラップされた関数を返します:

def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
ログイン後にコピー

あとは、add を timer And sub 関数でラップするだけです:

add = timer(add)
ログイン後にコピー

以上です!完全なコードは次のとおりです。

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
def add(x, y=10):
 return x + y
add = timer(add)
def sub(x, y=10):
 return x - y
sub = timer(sub)
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
Output:
time taken:0.0
add(10) 20
time taken:9.5367431640625e-07
add(20, 30) 50
time taken:0.0
add("a", "b") ab
time taken:9.5367431640625e-07
sub(10) 0
time taken:9.5367431640625e-07
sub(20, 30) -10
ログイン後にコピー

プロセスを要約しましょう。関数 (add 関数など) があり、その関数をアクション (タイミングなど) でラップします。パッケージ化の結果は、特定の新しい機能を実装できる新しい機能です。

もちろん、デフォルト値にはまだ問題があるため、後で修正します。

Decorator

さて、上記のソリューションはデコレータのアイデアに非常に近いものです。一般的な動作を使用して特定の関数をラップします。このパターンはデコレーションです。 . デバイスが何をしているのか。デコレータを使用した後のコードは次のとおりです。

def add(x, y=10):
 return x + y
add = timer(add)
You write:
@timer
def add(x, y=10):
 return x + y
ログイン後にコピー

これらの機能は同じであり、これは Python デコレータの機能です。実装する関数は add = timer(add) に似ていますが、デコレーターが構文を関数の上に置き、構文が @timer より単純である点が異なります。

# dec.py
from time import time
def timer(func):
 def f(x, y=10):
 before = time()
 rv = func(x, y)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
ログイン後にコピー

パラメータとキーワード パラメータ

ここで、解決されていない小さな問題がまだあります。タイマー関数では、パラメーター x と y をハードコーディングします。つまり、y のデフォルト値を 10 に指定します。引数とキーワード引数を関数に渡す方法 (*args と **kwargs) があります。パラメータは関数の標準パラメータ (この場合は x がパラメータ)、キーワード パラメータはすでにデフォルト値 (この場合は y=10) を持つパラメータです。コードは次のとおりです。

# dec.py
from time import time
def timer(func):
 def f(*args, **kwargs):
 before = time()
 rv = func(*args, **kwargs)
 after = time()
 print('time taken: ', after - before)
 return rv
 return f
@timer
def add(x, y=10):
 return x + y
@timer
def sub(x, y=10):
 return x - y
print('add(10)', add(10))
print('add(20, 30)', add(20, 30))
print('add("a", "b")', add("a", "b"))
print('sub(10)', sub(10))
print('sub(20, 30)', sub(20, 30))
ログイン後にコピー

これで、タイマー関数は、これらのパラメータを関数に渡すだけなので、任意の関数、任意のパラメータ、および任意のデフォルト値設定を処理できます。

高次デコレータ

関数を別の関数でラップして便利な動作を追加できれば、さらに一歩進められるのではないかと疑問に思うかもしれません。関数を別の関数でラップし、また別の関数によってラップされるのでしょうか? #########できる!実際、関数は必要に応じて深くすることができます。たとえば、関数を n 回実行するデコレーターを作成するとします。以下に示すように:

def ntimes(n):
 def inner(f):
 def wrapper(*args, **kwargs):
 for _ in range(n):
 rv = f(*args, **kwargs)
 return rv
 return wrapper
 return inner
ログイン後にコピー

その後、上記の関数を使用して、前の記事の add 関数などの別の関数をラップできます:

@ntimes(3)
def add(x, y):
 print(x + y)
 return x + y
ログイン後にコピー
出力ステートメントは、コードが実際に実行されたことを示しています。 3回。

以上がPython デコレータを 1 つの記事で理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:51cto.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート