検索
ホームページバックエンド開発Python チュートリアルPython でのデコレータの使用について詳しく学ぶ

デコレーターとデコレーターのパターン
まず第一に、デコレーターという言葉を使用すると、多くの心配が生じる可能性があることを誰もが理解する必要があります。これは、『デザイン パターン』という本のデコレーター パターンと混同されやすいためです。この新機能に関しては他の用語もいくつか検討されましたが、デコレータが優先されました。
確かに、Python デコレータを使用してデコレータ パターンを実装できることは事実ですが、これは間違いなくその機能のごく一部であり、少し無駄です。 Python デコレータにとって、これはマクロに最も近いものだと思います。

マクロの歴史
マクロの歴史は非常に長いですが、ほとんどの人はC言語の前処理マクロを使用した経験があるかもしれません。ただし、C 言語のマクロにはいくつかの問題があります。(1) マクロは C 言語には存在しません。(2) マクロの動作が少し奇妙で、C 言語の動作と異なる場合があります。 。
言語自体の一部の要素に対する操作をサポートするために、Java と C# の両方にアノテーションが追加されています。もちろん、それらにはすべていくつかの問題があります。場合によっては、目標を達成するために多くの落とし穴を回避しなければならないことがあります。まだ完成していませんが、これらの注釈機能は、これらの言語のいくつかの固有の機能によって制約されることになります (Martin Fowler が説明した「ディレクション」と同じように)
少し違うのは、私を含む多くの C++ プログラマーが C++ テンプレートの威力を認識しており、すでにこの機能をマクロのように使用していることです。
他の多くの言語にもマクロ関数が含まれていますが、私はそれらについてあまり知りませんが、関数の能力と豊富さの点で Python デコレータは Lisp マクロに非常に似ていると恥ずかしがらずに言いたいと思います。

マクロ目標
マクロを次のように説明するのは過言ではないと思います。マクロは、言語要素自体を操作する機能を提供するためにプログラミング言語に存在します。これはまさに Python デコレータが関数を変更してこのクラスを装飾できることです。複雑なメタクラスと比較して、単純なデコレータが提供されることが多いのはこれが理由かもしれません。
ほとんどのプログラミング言語が提供する自己修正 (メタプログラミング) ソリューションには大きな欠点があります。つまり、制限や制約が多すぎるため、他の言語を書いているような錯覚を与えます。
Python は、Martin Fowler が「可能にする」プログラミング言語と呼ぶものに準拠しています。では、変更操作 (メタプログラミング) を実行したい場合、なぜ「異なる」または「制限的な」言語を考え出す必要があるのでしょうか? Python をコピーして自分で作業を始めてみてはいかがでしょうか?これが Python デコレータでできることです。

Python デコレーターでできること
デコレーターを使用すると、関数またはクラスのコード (ロジック) を「挿入」または「変更」できます。デコレータは、よりシンプルかつ強力であることに加えて、AOP アスペクト指向プログラミングに少し似ていますね。たとえば、メソッドの開始または終了の前に実行したいことを追加します (アクセス許可のチェック、追跡、リソースのロックなど、アスペクト指向プログラミングでの一般的な操作など)。デコレータを使用すると、次のことができます:

リーリー

関数のデコレーター
関数デコレータは通常、次のような修飾デコレータを適用するために関数定義のコードの前に配置されます。

リーリー
コンパイラーがこのコードに到達すると、関数 aFunction がコンパイルされ、コンパイルされた関数オブジェクトが myDecorator に渡され、デコレーターは元の関数 aFunction を置き換える新しい関数オブジェクトを生成します。

では、デコレータ myDecorator のコード実装は何でしょうか?デコレータの入門例のほとんどは関数を記述していますが、クラススタイルのデコレータは関数型デコレータよりも理解しやすく、強力であることがわかります。
確認する必要がある唯一のことは、デコレータによって返されるオブジェクトが関数のように呼び出せるため、クラス デコレータは __call__ を実装する必要があるということです。
装飾者は何をすべきでしょうか?まあ、何でもできますが、通常は、必須ではありませんが、渡された元の関数がどこかで実行されることが期待されます。
リーリー

このコードを実行すると、次の出力が表示されます:

リーリー

请注意,myDecorator的构造器实际是在装饰函数的时候执行的。我们可以在__init__()里面调用函数f,能够看到,在装饰器被调用之前,函数调用f()就已经完成了。另外,装饰器的构造器能够接收被装饰的方法。一般来讲,我们会捕捉到这个函数对象然后接下来在函数__call__()里面调用。装饰和调用是两个非常清晰明了的不同的步骤,这也是我为什么说类似装饰器更简单同时也更强大的原因。
当函数aFunction被装饰完成然后调用的时候,我们得到了一个完全不同的行为,实际上执行的是myDecorator.__call__()的代码逻辑,这是因为”装饰“把原有的代码逻辑用新的返回的逻辑给替换掉了。在我们的例子中,myDecorator对象替换掉了函数aFunction。事实上,在装饰器操作符@被加入之前,你不得不做一些比较low的操作来完成同样的事情:

def foo(): pass
foo = staticmethod(foo)

因为有了@这个装饰器操作符, 你可以非常优雅的得到同样的结果:

@staticmethod
def foo(): pass

不过也有不少人因为这一点反对装饰器,不过@仅仅是一个很小的语法糖而已,把一个函数对象传递给另外一个函数,然后用返回值替换原有的方法。
我觉着,之所以装饰器会产生这么大的影响是因为这个小小的语法糖完全改变了人们思考编程的方式。的确,通过将它实现成一个编程语言结构,它将”代码应用到代码上面“的思想带到了主流编程思维层面。

青出于蓝
现在我们实现一下第一个例子。在这里我们将会做一些很常规的事情,并且会使用这些代码:

class entryExit(object):

  def __init__(self, f):
    self.f = f

  def __call__(self):
    print "Entering", self.f.__name__
    self.f()
    print "Exited", self.f.__name__

@entryExit
def func1():
  print "inside func1()"

@entryExit
def func2():
  print "inside func2()"

func1()
func2()

运行结果是:

Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2

现在我们能够看到,那些被装饰的方法有了“进入”和“离开”的跟踪信息。
构造器存储了通过参数传递进来的函数对象,在调用的方法里,我们用函数对象的__name__属性来展示被调用函数的名称,然后调用被装饰的函数自己。

使用函数作为装饰器
对于装饰器返回结果的约束只有一个,那就是能够被调用,从而它能够合理的替换掉原有的被装饰的那个函数。在上面的这些例子中,我们是将原有的函数用包含有__call__()的对象替换的。一个函数对象同样能够被调用,所以我们可以用函数来重写前一个装饰器的例子,像这样:

def entryExit(f):
  def new_f():
    print "Entering", f.__name__
    f()
    print "Exited", f.__name__
  return new_f

@entryExit
def func1():
  print "inside func1()"

@entryExit
def func2():
  print "inside func2()"

func1()
func2()
print func1.__name__


函数new_f()嵌套定义在entryExit的方法体里面,当entryExit被调用的时候,new_f()也会顺理成章地被返回。值得注意的是new_f()是一个闭包,捕获了参数变量f的值。
当new_f()定义完成后,它将会被entryExit返回,然后装饰器机制发生作用将结果赋值成被装饰的新方法。
代码print func1.__name__的输出结果是new_f,因为在装饰发生的过程中,原来的方法已经被替换成了new_f,如果对你来说这是一个问题的话,你可以在装饰器返回结果之前修改掉函数的名字:

def entryExit(f):
  def new_f():
    print "Entering", f.__name__
    f()
    print "Exited", f.__name__
  new_f.__name__ = f.__name__
  return new_f

你可以动态的获取函数的信息包括那些你做的更改,这在python里面非常有用。

带参数的装饰器
现在我们把上面的那个例子简单的改动一下,看看在添加装饰器参数的情况下会发生什么情况:

class decoratorWithArguments(object):

  def __init__(self, arg1, arg2, arg3):
    """
    If there are decorator arguments, the function
    to be decorated is not passed to the constructor!
    """
    print "Inside __init__()"
    self.arg1 = arg1
    self.arg2 = arg2
    self.arg3 = arg3

  def __call__(self, f):
    """
    If there are decorator arguments, __call__() is only called
    once, as part of the decoration process! You can only give
    it a single argument, which is the function object.
    """
    print "Inside __call__()"
    def wrapped_f(*args):
      print "Inside wrapped_f()"
      print "Decorator arguments:", self.arg1, self.arg2, self.arg3
      f(*args)
      print "After f(*args)"
    return wrapped_f

@decoratorWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
  print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "after first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "after second sayHello() call"

从输出结果来看,运行的效果发生了明显的变化:

Inside __init__()
Inside __call__()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

现在,在“装饰”阶段,构造器和__call__()都会被依次调用,__call__()也只接受一个函数对象类型的参数,而且必须返回一个装饰方法去替换原有的方法,__call__()只会在“装饰”阶段被调用一次,接着返回的装饰方法会被实际用在调用过程中。
尽管这个行为很合理,构造器现在被用来捕捉装饰器的参数,而且__call__()不能再被当做装饰方法,相反要利用它来完成装饰的过程。尽管如此,第一次见到这种与不带参数的装饰器迥然不同的行为还是会让人大吃一惊,而且它们的编程范式也有很大的不同。

带参数的函数式装饰器
最后,让我们看一下更复杂的函数式装饰器,在这里你不得不一次完成所有的事情:

def decoratorFunctionWithArguments(arg1, arg2, arg3):
  def wrap(f):
    print "Inside wrap()"
    def wrapped_f(*args):
      print "Inside wrapped_f()"
      print "Decorator arguments:", arg1, arg2, arg3
      f(*args)
      print "After f(*args)"
    return wrapped_f
  return wrap

@decoratorFunctionWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
  print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "after first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "after second sayHello() call"

输出结果:

Inside wrap()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

関数デコレーターの戻り値は、元のラップされた関数をラップできる関数である必要があります。つまり、Python は装飾が発生したときに返された関数の結果を取得して呼び出し、それを装飾された関数に渡します。これが、デコレーターの実装で定義された 3 つのネストされた関数の理由です。新しい置き換え機能。
クロージャの性質により、wrap_f() は、クラス デコレータの例のように値 arg1、arg2、および arg3 を明示的に格納しなくても、これらのパラメータにアクセスできます。ただし、これは私が「暗黙的よりも明示的な方が優れている」と感じる例です。関数型デコレータはもう少し合理化されているかもしれませんが、クラス デコレータは理解しやすいため、変更や保守が容易です。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Pythonと時間:勉強時間を最大限に活用するPythonと時間:勉強時間を最大限に活用するApr 14, 2025 am 12:02 AM

限られた時間でPythonの学習効率を最大化するには、PythonのDateTime、時間、およびスケジュールモジュールを使用できます。 1. DateTimeモジュールは、学習時間を記録および計画するために使用されます。 2。時間モジュールは、勉強と休息の時間を設定するのに役立ちます。 3.スケジュールモジュールは、毎週の学習タスクを自動的に配置します。

Python:ゲーム、GUIなどPython:ゲーム、GUIなどApr 13, 2025 am 12:14 AM

PythonはゲームとGUI開発に優れています。 1)ゲーム開発は、2Dゲームの作成に適した図面、オーディオ、その他の機能を提供し、Pygameを使用します。 2)GUI開発は、TKINTERまたはPYQTを選択できます。 TKINTERはシンプルで使いやすく、PYQTは豊富な機能を備えており、専門能力開発に適しています。

Python vs. C:比較されたアプリケーションとユースケースPython vs. C:比較されたアプリケーションとユースケースApr 12, 2025 am 12:01 AM

Pythonは、データサイエンス、Web開発、自動化タスクに適していますが、Cはシステムプログラミング、ゲーム開発、組み込みシステムに適しています。 Pythonは、そのシンプルさと強力なエコシステムで知られていますが、Cは高性能および基礎となる制御機能で知られています。

2時間のPython計画:現実的なアプローチ2時間のPython計画:現実的なアプローチApr 11, 2025 am 12:04 AM

2時間以内にPythonの基本的なプログラミングの概念とスキルを学ぶことができます。 1.変数とデータ型、2。マスターコントロールフロー(条件付きステートメントとループ)、3。機能の定義と使用を理解する4。

Python:主要なアプリケーションの調査Python:主要なアプリケーションの調査Apr 10, 2025 am 09:41 AM

Pythonは、Web開発、データサイエンス、機械学習、自動化、スクリプトの分野で広く使用されています。 1)Web開発では、DjangoおよびFlask Frameworksが開発プロセスを簡素化します。 2)データサイエンスと機械学習の分野では、Numpy、Pandas、Scikit-Learn、Tensorflowライブラリが強力なサポートを提供します。 3)自動化とスクリプトの観点から、Pythonは自動テストやシステム管理などのタスクに適しています。

2時間でどのくらいのPythonを学ぶことができますか?2時間でどのくらいのPythonを学ぶことができますか?Apr 09, 2025 pm 04:33 PM

2時間以内にPythonの基本を学ぶことができます。 1。変数とデータ型を学習します。2。ステートメントやループの場合などのマスター制御構造、3。関数の定義と使用を理解します。これらは、簡単なPythonプログラムの作成を開始するのに役立ちます。

プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は?プロジェクトの基本と問題駆動型の方法で10時間以内にコンピューター初心者プログラミングの基本を教える方法は?Apr 02, 2025 am 07:18 AM

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

中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか?中間の読書にどこでもfiddlerを使用するときにブラウザによって検出されないようにするにはどうすればよいですか?Apr 02, 2025 am 07:15 AM

fiddlereveryversings for the-middleの測定値を使用するときに検出されないようにする方法

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

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

ホットツール

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

SublimeText3 中国語版

SublimeText3 中国語版

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

EditPlus 中国語クラック版

EditPlus 中国語クラック版

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