###############概要###
記事「Python デコレータの詳細」では、Python デコレータの概念を紹介し、多くの優れたデコレータをデモンストレーションし、その使用方法を説明しました。
デコレータについて何も知らない場合は、ここで簡単に復習してください。デコレーターは、呼び出し可能オブジェクト (関数、メソッド、クラス、または
call() メソッドを持つオブジェクト) であり、呼び出し可能オブジェクトを入力として受け取り、呼び出し可能オブジェクトを出力として返します。通常、返された呼び出し可能オブジェクトは、入力呼び出し可能オブジェクトを呼び出す前および/または呼び出した後に、いくつかの操作を実行します。 @
構文を使用してデコレーターを適用できます。多くの例が近日公開予定です...Hello World デコレータ
「Hello world!」デコレータから始めましょう。このデコレータは、装飾された呼び出し可能関数を、単に「Hello World!」を出力する関数に完全に置き換えます。
関数に
@hello_worldという注釈を付け、
hello_worldデコレータで装飾しましょう。
リーリー 引数 (間違ったデータ型や引数の数を含む) を指定して multiply
を呼び出すと、結果は常に「Hello World!」と表示されます。
リーリー
###わかりました。どのように機能するのでしょうか?元の乗算関数は、hello_world デコレータ内のネストされた装飾関数に完全に置き換えられます。 hello_world
f (この単純なデコレーターでは使用されていません) を受け入れ、これが という名前のネストされた A 関数を定義していることがわかります。 Decorated
は、引数とキーワード引数の任意の組み合わせ (defdecorated(*args, **kwargs)) を受け入れ、最終的に decorated 関数を返します。
関数およびメソッドのデコレータの作成
関数デコレータとメソッド デコレータの作成に違いはありません。デコレータの定義も同様です。入力呼び出し可能オブジェクトは、通常の関数またはバインドされたメソッドになります。
検証してみましょう。これは、呼び出し可能入力を出力するだけのデコレータであり、呼び出す前に型を入力します。これは、デコレータが何かを実行し、元の呼び出し可能オブジェクトを呼び出し続ける典型的なケースです。
リーリー
最後の行では、入力呼び出し可能オブジェクトを一般的な方法で呼び出し、結果を返すことに注意してください。このデコレーターは、動作中のアプリケーション内の任意の関数またはメソッドを装飾できるため、非侵入的です。装飾された関数は元の関数を呼び出し、以前は副作用が生じるだけであるため、アプリケーションは引き続き動作します。
リーリー
関数とメソッドを呼び出すと、呼び出し可能オブジェクトが出力され、元のタスクが実行されます。リーリー
パラメータ付きデコレータデコレータはパラメータも受け入れることができます。デコレータ操作を構成するこの機能は非常に強力であり、多くのコンテキストで同じデコレータを使用できるようになります。
あなたがコーディングのスピードが速すぎて、他のチームメンバーに悪印象を与えているため、上司から速度を落とすように頼まれたとします。関数の実行にかかる時間を測定するデコレータを作成しましょう。関数の実行にかかる時間が特定の秒数
t
未満の場合、t 秒が経過するまで待機してから戻ります。今の違いは、デコレータ自体がパラメータ
t
を受け入れて最小実行時間を決定し、異なる関数を異なる最小実行時間でデコレートできることです。また、デコレータ パラメータを導入するときは、2 つのレベルのネストが必要であることに気づくでしょう:リーリー
開けてみましょう。デコレーター自体 - 関数minimum_runtime はパラメーター t
を受け取り、これは装飾された呼び出し可能関数の最小実行時間を表します。入力呼び出し可能f はネストされた デコレーター
関数に「プッシュダウン」され、入力呼び出し可能引数は別のネストされた関数ラッパー に「プッシュダウン」されます。 # 実際のロジックは ラッパー 関数内で発生します。開始時刻を記録し、元の呼び出し可能な f を引数とともに呼び出し、結果を保存します。次に、ランタイムをチェックし、それが mint 未満の場合は、残りの時間スリープしてから戻ります。 强>それをテストするために、乗算を呼び出し、それらをさまざまな遅延で修飾する関数をいくつか作成します。
@minimum_runtime(1) def slow_multiply(x, y): multiply(x, y) @minimum_runtime(3) def slower_multiply(x, y): multiply(x, y)
现在,我将直接调用 multiply 以及较慢的函数并测量时间。
import time funcs = [multiply, slow_multiply, slower_multiply] for f in funcs: start = time.time() f(6, 7) print f, time.time() - start
这是输出:
42 1.59740447998e-05 42 1.00477004051 42 3.00489807129
正如您所看到的,原始乘法几乎没有花费任何时间,并且较慢的版本确实根据提供的最小运行时间进行了延迟。
另一个有趣的事实是,执行的装饰函数是包装器,如果您遵循装饰的定义,这是有意义的。但这可能是一个问题,特别是当我们处理堆栈装饰器时。原因是许多装饰器还会检查其输入可调用对象并检查其名称、签名和参数。以下部分将探讨此问题并提供最佳实践建议。
对象装饰器
您还可以使用对象作为装饰器或从装饰器返回对象。唯一的要求是它们有一个 __call__() 方法,因此它们是可调用的。下面是一个基于对象的装饰器的示例,它计算其目标函数被调用的次数:
class Counter(object): def __init__(self, f): self.f = f self.called = 0 def __call__(self, *args, **kwargs): self.called += 1 return self.f(*args, **kwargs)
这是在行动:
@Counter def bbb(): print 'bbb' bbb() bbb bbb() bbb bbb() bbb print bbb.called 3
在基于函数的装饰器和基于对象的装饰器之间进行选择
这主要是个人喜好问题。嵌套函数和函数闭包提供了对象提供的所有状态管理。有些人对类和对象感觉更自在。
在下一节中,我将讨论行为良好的装饰器,而基于对象的装饰器需要一些额外的工作才能表现良好。
行为良好的装饰器
通用装饰器通常可以堆叠。例如:
@decorator_1 @decorator_2 def foo(): print 'foo() here'
当堆叠装饰器时,外部装饰器(本例中为decorator_1)将接收内部装饰器(decorator_2)返回的可调用对象。如果decorator_1在某种程度上依赖于原始函数的名称、参数或文档字符串,并且decorator_2是简单实现的,那么decorator_2将看不到原始函数中的正确信息,而只能看到decorator_2返回的可调用信息。
例如,下面是一个装饰器,它验证其目标函数的名称是否全部小写:
def check_lowercase(f): def decorated(*args, **kwargs): assert f.func_name == f.func_name.lower() f(*args, **kwargs) return decorated
让我们用它来装饰一个函数:
@check_lowercase def Foo(): print 'Foo() here'
调用 Foo() 会产生断言:
In [51]: Foo() --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) ipython-input-51-bbcd91f35259 in module() ----> 1 Foo() ipython-input-49-a80988798919 in decorated(*args, **kwargs) 1 def check_lowercase(f): 2 def decorated(*args, **kwargs): ----> 3 assert f.func_name == f.func_name.lower() 4 return decorated
但是,如果我们将 check_lowercase 装饰器堆叠在像 hello_world 这样返回名为“decorated”的嵌套函数的装饰器上,结果会非常不同:
@check_lowercase @hello_world def Foo(): print 'Foo() here' Foo() Hello World!
check_lowercase 装饰器没有引发断言,因为它没有看到函数名称“Foo”。这是一个严重的问题。装饰器的正确行为是尽可能多地保留原始函数的属性。
让我们看看它是如何完成的。现在,我将创建一个 shell 装饰器,它仅调用其输入可调用函数,但保留输入函数中的所有信息:函数名称、其所有属性(如果内部装饰器添加了一些自定义属性)及其文档字符串。 p>
def passthrough(f): def decorated(*args, **kwargs): f(*args, **kwargs) decorated.__name__ = f.__name__ decorated.__name__ = f.__module__ decorated.__dict__ = f.__dict__ decorated.__doc__ = f.__doc__ return decorated
现在,堆叠在passthrough装饰器之上的装饰器将像直接装饰目标函数一样工作。
@check_lowercase @passthrough def Foo(): print 'Foo() here'
使用@wraps装饰器
此功能非常有用,以至于标准库在 functools 模块中有一个名为“wraps”的特殊装饰器,可以帮助编写与其他装饰器配合良好的适当装饰器。您只需在装饰器中使用 @wraps(f) 装饰返回的函数即可。看看使用 wraps 时 passthrough 看起来有多简洁:
from functools import wraps def passthrough(f): @wraps(f) def decorated(*args, **kwargs): f(*args, **kwargs) return decorated
我强烈建议始终使用它,除非您的装饰器旨在修改其中一些属性。
编写类装饰器
类装饰器是在 Python 3.0 中引入的。他们对整个班级进行操作。定义类时和创建任何实例之前会调用类装饰器。这使得类装饰器几乎可以修改类的每个方面。通常您会添加或修饰多个方法。
让我们直接跳到一个奇特的示例:假设您有一个名为“AwesomeClass”的类,其中包含一堆公共方法(名称不以下划线开头的方法,例如 init),并且您有一个基于单元测试的测试类,名为“AwesomeClassTest”。 AwesomeClass 不仅很棒,而且非常关键,您要确保如果有人向 AwesomeClass 添加新方法,他们也会向 AwesomeClassTest 添加相应的测试方法。这是 AwesomeClass:
class AwesomeClass: def awesome_1(self): return 'awesome!' def awesome_2(self): return 'awesome! awesome!'
这是 AwesomeClassTest:
from unittest import TestCase, main class AwesomeClassTest(TestCase): def test_awesome_1(self): r = AwesomeClass().awesome_1() self.assertEqual('awesome!', r) def test_awesome_2(self): r = AwesomeClass().awesome_2() self.assertEqual('awesome! awesome!', r) if __name__ == '__main__': main()
现在,如果有人添加带有错误的 awesome_3 方法,测试仍然会通过,因为没有调用 awesome_3 的测试。
如何确保每个公共方法始终都有一个测试方法?好吧,当然,你编写一个类装饰器。 @ensure_tests 类装饰器将装饰 AwesomeClassTest 并确保每个公共方法都有相应的测试方法。
def ensure_tests(cls, target_class): test_methods = [m for m in cls.__dict__ if m.startswith('test_')] public_methods = [k for k, v in target_class.__dict__.items() if callable(v) and not k.startswith('_')] # Strip 'test_' prefix from test method names test_methods = [m[5:] for m in test_methods] if set(test_methods) != set(public_methods): raise RuntimeError('Test / public methods mismatch!') return cls
这看起来不错,但有一个问题。类装饰器只接受一个参数:被装饰的类。 Ensure_tests 装饰器需要两个参数:类和目标类。我找不到一种方法来让类装饰器具有类似于函数装饰器的参数。没有恐惧。 Python 有 functools.partial 函数专门用于这些情况。
@partial(ensure_tests, target_class=AwesomeClass) class AwesomeClassTest(TestCase): def test_awesome_1(self): r = AwesomeClass().awesome_1() self.assertEqual('awesome!', r) def test_awesome_2(self): r = AwesomeClass().awesome_2() self.assertEqual('awesome! awesome!', r) if __name__ == '__main__': main()
运行测试会成功,因为所有公共方法 awesome_1 和 awesome_2 都有相应的测试方法 test_awesome_1 和 test_awesome_2。
---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
让我们添加一个没有相应测试的新方法awesome_3,然后再次运行测试。
class AwesomeClass: def awesome_1(self): return 'awesome!' def awesome_2(self): return 'awesome! awesome!' def awesome_3(self): return 'awesome! awesome! awesome!'
再次运行测试会产生以下输出:
python3 a.py Traceback (most recent call last): File "a.py", line 25, in module class AwesomeClassTest(TestCase): File "a.py", line 21, in ensure_tests raise RuntimeError('Test / public methods mismatch!') RuntimeError: Test / public methods mismatch!
类装饰器检测到不匹配并大声清晰地通知您。
结论
编写 Python 装饰器非常有趣,可以让您以可重用的方式封装大量功能。要充分利用装饰器并以有趣的方式组合它们,您需要了解最佳实践和习惯用法。 Python 3 中的类装饰器通过自定义完整类的行为添加了一个全新的维度。
以上が独自の Python デコレータを作成するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

PythonとCにはそれぞれ独自の利点があり、選択はプロジェクトの要件に基づいている必要があります。 1)Pythonは、簡潔な構文と動的タイピングのため、迅速な開発とデータ処理に適しています。 2)Cは、静的なタイピングと手動メモリ管理により、高性能およびシステムプログラミングに適しています。

PythonまたはCの選択は、プロジェクトの要件に依存します。1)迅速な開発、データ処理、およびプロトタイプ設計が必要な場合は、Pythonを選択します。 2)高性能、低レイテンシ、および緊密なハードウェアコントロールが必要な場合は、Cを選択します。

毎日2時間のPython学習を投資することで、プログラミングスキルを効果的に改善できます。 1.新しい知識を学ぶ:ドキュメントを読むか、チュートリアルを見る。 2。練習:コードと完全な演習を書きます。 3。レビュー:学んだコンテンツを統合します。 4。プロジェクトの実践:実際のプロジェクトで学んだことを適用します。このような構造化された学習計画は、Pythonを体系的にマスターし、キャリア目標を達成するのに役立ちます。

2時間以内にPythonを効率的に学習する方法は次のとおりです。1。基本的な知識を確認し、Pythonのインストールと基本的な構文に精通していることを確認します。 2。変数、リスト、関数など、Pythonのコア概念を理解します。 3.例を使用して、基本的および高度な使用をマスターします。 4.一般的なエラーとデバッグテクニックを学習します。 5.リストの概念を使用したり、PEP8スタイルガイドに従ったりするなど、パフォーマンスの最適化とベストプラクティスを適用します。

Pythonは初心者やデータサイエンスに適しており、Cはシステムプログラミングとゲーム開発に適しています。 1. Pythonはシンプルで使いやすく、データサイエンスやWeb開発に適しています。 2.Cは、ゲーム開発とシステムプログラミングに適した、高性能と制御を提供します。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

Pythonはデータサイエンスと迅速な発展により適していますが、Cは高性能およびシステムプログラミングにより適しています。 1. Python構文は簡潔で学習しやすく、データ処理と科学的コンピューティングに適しています。 2.Cには複雑な構文がありますが、優れたパフォーマンスがあり、ゲーム開発とシステムプログラミングでよく使用されます。

Pythonを学ぶために1日2時間投資することは可能です。 1.新しい知識を学ぶ:リストや辞書など、1時間で新しい概念を学びます。 2。練習と練習:1時間を使用して、小さなプログラムを書くなどのプログラミング演習を実行します。合理的な計画と忍耐力を通じて、Pythonのコアコンセプトを短時間で習得できます。

Pythonは学習と使用が簡単ですが、Cはより強力ですが複雑です。 1。Python構文は簡潔で初心者に適しています。動的なタイピングと自動メモリ管理により、使いやすくなりますが、ランタイムエラーを引き起こす可能性があります。 2.Cは、高性能アプリケーションに適した低レベルの制御と高度な機能を提供しますが、学習しきい値が高く、手動メモリとタイプの安全管理が必要です。


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

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