Pythonデコレーターの詳しい使い方紹介(コード例)
この記事では、Python デコレータの詳細な使用方法 (コード例) を紹介します。一定の参考価値があります。必要な友人は参照してください。お役に立てば幸いです。
Python では、通常、デコレータは関数を修飾してパブリック関数を実装し、コードの再利用を実現するために使用されます。関数定義の前に @xxxx を追加すると、関数は特定の動作を挿入します。これは驚くべきことです。ただし、これは単なる糖衣構文です。
シナリオ
データを別の方法で処理するために使用される機能する関数がいくつかあると仮定します。
def work_bar(data): pass def work_foo(data): pass
これを関数呼び出しの前後に実行したいと考えています。ログを出力したいのですがどうすればいいですか?
愚かな解決策
logging.info('begin call work_bar') work_bar(1) logging.info('call work_bar done')
複数のコード呼び出しがある場合はどうなりますか?考えるだけで怖いです!
関数のパッケージ化
愚かな解決策は、コードの冗長性を高めすぎることであり、各関数呼び出しを再度記述する必要がありますロギング
。冗長ロジックのこの部分は、新しい関数にカプセル化できます:
def smart_work_bar(data): logging.info('begin call: work_bar') work_bar(data) logging.info('call doen: work_bar')
このようにして、smart_work_bar
を毎回呼び出すことができます:
smart_work_bar(1) # ... smart_work_bar(some_data)
General Closure
完璧に見えます...しかし、work_foo
にも同じニーズがある場合、smart_work_foo
を再度実装する必要がありますか?これは明らかに非科学的です!
心配しないでください。クロージャを使用できます。
def log_call(func): def proxy(*args, **kwargs): logging.info('begin call: {name}'.format(name=func.func_name)) result = func(*args, **kwargs) logging.info('call done: {name}'.format(name=func.func_name)) return result return proxy
この関数は、関数オブジェクト (プロキシ関数) をパラメータとして受け取り、プロキシ関数を返します。プロキシ関数を呼び出すと、最初にログが出力され、次にプロキシ関数が呼び出され、呼び出しが完了してからログが出力され、最後に呼び出し結果が返されます。このようにして、一般化の目的は達成されるのではないでしょうか? ——任意のプロキシ関数 func
、log_call
を簡単に処理できます。
smart_work_bar = log_call(work_bar) smart_work_foo = log_call(work_foo) smart_work_bar(1) smart_work_foo(1) # ... smart_work_bar(some_data) smart_work_foo(some_data)
行 1
で、log_call
はパラメータ work_bar
を受け取り、プロキシ関数 proxy
を返し、それを割り当てます。 smart_work_bar
まで。 4
行目では、プロキシ関数 proxy
である smart_work_bar
を呼び出し、最初にログを出力し、次に func
を呼び出します。 work_bar
、最後にログを出力します。プロキシ関数では、func
が渡された work_bar
オブジェクトと密接に関連していることに注意してください。これは クロージャ です。
繰り返しますが、プロキシ関数名を上書きできます。新しい名前の前に smart_
を付けるのは、まだ少し面倒です:
work_bar = log_call(work_bar) work_foo = log_call(work_foo) work_bar(1) work_foo(1)
構文シュガー
まず次のコードを見てください。
def work_bar(data): pass work_bar = log_call(work_bar) def work_foo(data): pass work_foo = log_call(work_foo)
コードは冗長ではありませんが、まだ十分に直観的ではありません。このとき、構文シュガーが登場します~~~
@log_call def work_bar(data): pass
ですので、1 つのことに注意してください (強調 )。ここでの @log_call
の関数は次のとおりです。 Python
コンパイラはコードwork_bar = log_call(work_bar)
を挿入します。
評価デコレータ
まずはデコレータ eval_now
が何をするのか推測してみましょう。
def eval_now(func): return func()
奇妙に思えます。プロキシ関数が定義されていません。これはデコレータとみなされますか?
@eval_now def foo(): return 1 print foo
このコードは、関数を呼び出して評価するための 1
を出力します。それで何の役に立つの? foo = 1
と直接書くことはできないのでしょうか?この簡単な例では、このように書くことももちろん可能です。より複雑な例を見てみましょう - ログ オブジェクトの初期化:
# some other code before... # log format formatter = logging.Formatter( '[%(asctime)s] %(process)5d %(levelname) 8s - %(message)s', '%Y-%m-%d %H:%M:%S', ) # stdout handler stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler.setFormatter(formatter) stdout_handler.setLevel(logging.DEBUG) # stderr handler stderr_handler = logging.StreamHandler(sys.stderr) stderr_handler.setFormatter(formatter) stderr_handler.setLevel(logging.ERROR) # logger object logger = logging.Logger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(stdout_handler) logger.addHandler(stderr_handler) # again some other code after...
Use eval_now
:
# some other code before... @eval_now def logger(): # log format formatter = logging.Formatter( '[%(asctime)s] %(process)5d %(levelname) 8s - %(message)s', '%Y-%m-%d %H:%M:%S', ) # stdout handler stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler.setFormatter(formatter) stdout_handler.setLevel(logging.DEBUG) # stderr handler stderr_handler = logging.StreamHandler(sys.stderr) stderr_handler.setFormatter(formatter) stderr_handler.setLevel(logging.ERROR) # logger object logger = logging.Logger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(stdout_handler) logger.addHandler(stderr_handler) return logger # again some other code after...
2 つのコードの目的は同じですが、後者の方が明らかに明瞭で、コード ブロックのスタイルを持っています。さらに重要なのは、一時変数 (formatter
など) が外部名前空間 (グローバルなど) を汚染するのを避けるために、関数呼び出しはローカル名前空間で初期化されることです。
パラメータを使用したデコレータ
遅い関数呼び出しを記録するためのデコレータを定義します:
def log_slow_call(func): def proxy(*args, **kwargs): start_ts = time.time() result = func(*args, **kwargs) end_ts = time.time() seconds = start_ts - end_ts if seconds > 1: logging.warn('slow call: {name} in {seconds}s'.format( name=func.func_name, seconds=seconds, )) return result return proxy
3
、5
行は、関数呼び出しの前後で現在時刻を取得し、7
行で呼び出し時間を計算しており、1 秒以上かかる場合は警告ログが出力されます。
@log_slow_call def sleep_seconds(seconds): time.sleep(seconds) sleep_seconds(0.1) # 没有日志输出 sleep_seconds(2) # 输出警告日志
ただし、しきい値の設定は常に状況に依存し、機能ごとに異なる値が設定される場合があります。しきい値をパラメータ化する方法があれば良いでしょう:
def log_slow_call(func, threshold=1): def proxy(*args, **kwargs): start_ts = time.time() result = func(*args, **kwargs) end_ts = time.time() seconds = start_ts - end_ts if seconds > threshold: logging.warn('slow call: {name} in {seconds}s'.format( name=func.func_name, seconds=seconds, )) return result return proxy
ただし、 @xxxx
構文シュガーは常に、パラメータとして装飾された関数を使用してデコレータを呼び出します。 しきい値
パラメータを渡す可能性はありません。どうやってするの? ——クロージャを使用して、threshold
パラメータをカプセル化します:
def log_slow_call(threshold=1): def decorator(func): def proxy(*args, **kwargs): start_ts = time.time() result = func(*args, **kwargs) end_ts = time.time() seconds = start_ts - end_ts if seconds > threshold: logging.warn('slow call: {name} in {seconds}s'.format( name=func.func_name, seconds=seconds, )) return result return proxy return decorator @log_slow_call(threshold=0.5) def sleep_seconds(seconds): time.sleep(seconds)
このように、log_slow_call(threshold=0.5)
は戻り関数 decorator## を呼び出します。 #、関数にはクロージャ変数
threshold があり、値は
0.5 です。
decorator模様替え
sleep_秒。
@log_slow_call() def sleep_seconds(seconds): time.sleep(seconds)
def log_slow_call(func=None, threshold=1): def decorator(func): def proxy(*args, **kwargs): start_ts = time.time() result = func(*args, **kwargs) end_ts = time.time() seconds = start_ts - end_ts if seconds > threshold: logging.warn('slow call: {name} in {seconds}s'.format( name=func.func_name, seconds=seconds, )) return result return proxy if func is None: return decorator else: return decorator(func)
A デフォルトしきい値 (呼び出しなし) と使用法
B カスタムしきい値 (呼び出しあり) という 2 つの異なる使用法と互換性があります。
# Case A @log_slow_call def sleep_seconds(seconds): time.sleep(seconds) # Case B @log_slow_call(threshold=0.5) def sleep_seconds(seconds): time.sleep(seconds)
A、何が起こるかというと
log_slow_call(sleep_seconds) です。つまり、
func パラメータは空ではありません。これは直接的なものです。調整
decoratorラップして返します (しきい値はデフォルトです)。
用法B
中,先发生的是log_slow_call(threshold=0.5)
,func
参数为空,直接返回新的装饰器decorator
,关联闭包变量threshold
,值为0.5
;然后,decorator
再装饰函数sleep_seconds
,即decorator(sleep_seconds)
。注意到,此时threshold
关联的值是0.5
,完成定制化。
你可能注意到了,这里最好使用关键字参数这种调用方式——使用位置参数会很丑陋:
# Case B- @log_slow_call(None, 0.5) def sleep_seconds(seconds): time.sleep(seconds)
当然了,函数调用尽量使用关键字参数是一种极佳实践,含义清晰,在参数很多的情况下更是如此。
智能装饰器
上节介绍的写法,嵌套层次较多,如果每个类似的装饰器都用这种方法实现,还是比较费劲的(脑子不够用),也比较容易出错。
假设有一个智能装饰器smart_decorator
,修饰装饰器log_slow_call
,便可获得同样的能力。这样,log_slow_call
定义将变得更清晰,实现起来也更省力啦:
@smart_decorator def log_slow_call(func, threshold=1): def proxy(*args, **kwargs): start_ts = time.time() result = func(*args, **kwargs) end_ts = time.time() seconds = start_ts - end_ts if seconds > threshold: logging.warn('slow call: {name} in {seconds}s'.format( name=func.func_name, seconds=seconds, )) return result return proxy
脑洞开完,smart_decorator
如何实现呢?其实也简单:
def smart_decorator(decorator): def decorator_proxy(func=None, **kwargs): if func is not None: return decorator(func=func, **kwargs) def decorator_proxy(func): return decorator(func=func, **kwargs) return decorator_proxy return decorator_proxy
smart_decorator
实现了以后,设想就成立了!这时,log_slow_call
,就是decorator_proxy
(外层),关联的闭包变量decorator
是本节最开始定义的log_slow_call
(为了避免歧义,称为real_log_slow_call
)。log_slow_call
支持以下各种用法:
# Case A @log_slow_call def sleep_seconds(seconds): time.sleep(seconds)
用法A
中,执行的是decorator_proxy(sleep_seconds)
(外层),func
非空,kwargs
为空;直接执行decorator(func=func, **kwargs)
,即real_log_slow_call(sleep_seconds)
,结果是关联默认参数的proxy
。
# Case B # Same to Case A @log_slow_call() def sleep_seconds(seconds): time.sleep(seconds)
用法B
中,先执行decorator_proxy()
,func
及kwargs
均为空,返回decorator_proxy
对象(内层);再执行decorator_proxy(sleep_seconds)
(内层);最后执行decorator(func, **kwargs)
,等价于real_log_slow_call(sleep_seconds)
,效果与用法A
一致。
# Case C @log_slow_call(threshold=0.5) def sleep_seconds(seconds): time.sleep(seconds)
用法C
中,先执行decorator_proxy(threshold=0.5)
,func
为空但kwargs
非空,返回decorator_proxy
对象(内层);再执行decorator_proxy(sleep_seconds)
(内层);最后执行decorator(sleep_seconds, **kwargs)
,等价于real_log_slow_call(sleep_seconds, threshold=0.5)
,阈值实现自定义!
以上がPythonデコレーターの詳しい使い方紹介(コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

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

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

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

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

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

ホットトピック









VSコードはWindows 8で実行できますが、エクスペリエンスは大きくない場合があります。まず、システムが最新のパッチに更新されていることを確認してから、システムアーキテクチャに一致するVSコードインストールパッケージをダウンロードして、プロンプトとしてインストールします。インストール後、一部の拡張機能はWindows 8と互換性があり、代替拡張機能を探すか、仮想マシンで新しいWindowsシステムを使用する必要があることに注意してください。必要な拡張機能をインストールして、適切に動作するかどうかを確認します。 Windows 8ではVSコードは実行可能ですが、開発エクスペリエンスとセキュリティを向上させるために、新しいWindowsシステムにアップグレードすることをお勧めします。

VSコード拡張機能は、悪意のあるコードの隠れ、脆弱性の活用、合法的な拡張機能としての自慰行為など、悪意のあるリスクを引き起こします。悪意のある拡張機能を識別する方法には、パブリッシャーのチェック、コメントの読み取り、コードのチェック、およびインストールに注意してください。セキュリティ対策には、セキュリティ認識、良好な習慣、定期的な更新、ウイルス対策ソフトウェアも含まれます。

VSコードでは、次の手順を通じて端末でプログラムを実行できます。コードを準備し、統合端子を開き、コードディレクトリが端末作業ディレクトリと一致していることを確認します。プログラミング言語(pythonのpython your_file_name.pyなど)に従って実行コマンドを選択して、それが正常に実行されるかどうかを確認し、エラーを解決します。デバッガーを使用して、デバッグ効率を向上させます。

PHPはWeb開発と迅速なプロトタイピングに適しており、Pythonはデータサイエンスと機械学習に適しています。 1.PHPは、単純な構文と迅速な開発に適した動的なWeb開発に使用されます。 2。Pythonには簡潔な構文があり、複数のフィールドに適しており、強力なライブラリエコシステムがあります。

PHPは主に手順プログラミングですが、オブジェクト指向プログラミング(OOP)もサポートしています。 Pythonは、OOP、機能、手続き上のプログラミングなど、さまざまなパラダイムをサポートしています。 PHPはWeb開発に適しており、Pythonはデータ分析や機械学習などのさまざまなアプリケーションに適しています。

VSコードはPythonの書き込みに使用でき、Pythonアプリケーションを開発するための理想的なツールになる多くの機能を提供できます。ユーザーは以下を可能にします。Python拡張機能をインストールして、コードの完了、構文の強調表示、デバッグなどの関数を取得できます。デバッガーを使用して、コードを段階的に追跡し、エラーを見つけて修正します。バージョンコントロールのためにGitを統合します。コードフォーマットツールを使用して、コードの一貫性を維持します。糸くずツールを使用して、事前に潜在的な問題を発見します。

VSコードはMacで利用できます。強力な拡張機能、GIT統合、ターミナル、デバッガーがあり、豊富なセットアップオプションも提供しています。ただし、特に大規模なプロジェクトまたは非常に専門的な開発の場合、コードと機能的な制限がある場合があります。

VSコードでJupyterノートブックを実行するための鍵は、Python環境が適切に構成されていることを確認し、コードの実行順序がセルの順序と一致していることを理解し、パフォーマンスに影響を与える可能性のある大きなファイルまたは外部ライブラリに注意することです。 VSコードで提供されるコードの完了とデバッグ機能は、コーディング効率を大幅に改善し、エラーを減らすことができます。
