Python デコレータは、Python の非常に便利な構造です。 Python でデコレータを使用すると、関数を別の関数内にラップすることで関数の動作を変更できます。デコレータを使用すると、よりクリーンなコードを記述し、機能を共有できるようになります。この記事では、デコレータの使用方法だけでなく、デコレータの作成方法についても説明します。
前提知識
Python のデコレータのトピックには、ある程度の背景知識が必要です。以下に、このチュートリアルを理解するためにすでに理解しておく必要があるいくつかの概念をリストします。必要に応じて概念をブラッシュアップできるリソースへのリンクもあります。
基本的な Python
このトピックは、より中級/上級のトピックです。そのため、学習を始める前に、データ型、関数、オブジェクト、クラスなどの Python の基本をすでに理解している必要があります。
また、ゲッター、セッター、コンストラクターなどのオブジェクト指向の概念も理解する必要があります。 Python プログラミング言語に詳しくない場合は、始めるためのリソースをいくつか紹介します。
職務は第一級市民である
基本的な Python に加えて、Python のより高度な概念についても知っておく必要があります。関数、および Python の他のほとんどすべてのものは、
int
や
string
のようなオブジェクトです。これらはオブジェクトであるため、次のようないくつかの操作を行うことができます。
-
string
またはint
関数の引数として渡すのと同じ方法で、関数を引数として別の関数に渡すことができます。 -
他の
string
またはint
値を返す場合と同様に、他の関数によって関数を返すこともできます。 - 関数は変数に格納できる
実際、関数オブジェクトと他のオブジェクトの唯一の違いは、関数オブジェクトにはマジック メソッド
__call__()
が含まれていることです。
この時点で、前提条件となる知識を理解していただければ幸いです。主要なトピックについて議論を始めましょう。
Python デコレータとは何ですか?
Python デコレーターは、単に関数を引数として取り込み、渡された関数の修正バージョンを返す関数です。言い換えると、関数 foo が引数として関数
bar
を取り込む場合、関数 foo はデコレーターになります。そして別の関数
baz
を返します。
関数
baz
baz
の本体内で関数
bar
への呼び出しがあるという意味で、
bar
を変更したものです。ただし、
bar
の呼び出しの前後では、
baz
何でもできます。それは一口でした。状況を説明するコードを次に示します。
# Foo is a decorator, it takes in another function, bar as an argument
def foo(bar):
# Here we create baz, a modified version of bar
# baz will call bar but can do anything before and after the function call
def baz():
# Before calling bar, we print something
print("Something")
# Then we run bar by making a function call
bar()
# Then we print something else after running bar
print("Something else")
# Lastly, foo returns baz, a modified version of bar
return baz
Python でデコレータを作成するには?
Python でデコレーターがどのように作成され、使用されるかを説明するために、簡単な例で説明します。この例では、関数が実行されるたびに、装飾している関数の名前をログに記録するロガー デコレーター関数を作成します。
まず、デコレータ関数を作成しました。デコレータは引数として
func
を受け取ります。
func
は装飾している関数です。
def create_logger(func):
# The function body goes here
デコレーター関数内で、
func
を実行する前に
func
の名前をログに記録する変更された関数を作成します。
# Inside create_logger
def modified_func():
print("Calling: ", func.__name__)
func()
次に、
create_logger
関数は変更された関数を返します。その結果、
create_logger
関数全体は次のようになります。
def create_logger(func):
def modified_func():
print("Calling: ", func.__name__)
func()
return modified_function
デコレータの作成が完了しました。
create_logger
関数は、デコレータ関数の簡単な例です。これは、装飾している関数である
func
を受け取り、別の関数
modified_func
を返します。
modified_func
func
を実行する前に、まず
func
の名前をログに記録します。
Python でデコレータを使用する方法
デコレーターを使用するには、次のように
@
構文を使用します。
@create_logger
def say_hello():
print("Hello, World!")
これで、スクリプト内でsay_hello()を呼び出すことができ、出力は次のテキストになるはずです。
Calling: say_hello
"Hello, World"
しかし、
@create_
create_logger は何をしているのでしょうか?そうですね、say_hello 関数にデコレータを適用しています。何をしているのかをよりよく理解するために、この段落のすぐ下のコードは、
say_hello
の前に
@create_logger
を置くのと同じ結果を達成します。
def say_hello():
print("Hello, World!")
say_hello = create_logger(say_hello)
つまり、Python でデコレータを使用する 1 つの方法は、上記のコードで行ったように、関数を渡してデコレータを明示的に呼び出すことです。もう 1 つのより簡潔な方法は、
@
構文を使用することです。
このセクションでは、Python デコレータの作成方法について説明しました。
もう少し複雑な例
上記の例は単純なケースでした。装飾している関数が引数を受け取る場合など、もう少し複雑な例があります。もう 1 つのより複雑な状況は、クラス全体を装飾したい場合です。ここではこれら両方の状況について説明します。
関数が引数を受け取るとき
デコレートしている関数が引数を受け取る場合、変更された関数は引数を受け取り、最終的に未変更の関数を呼び出すときにそれらを渡す必要があります。わかりにくいと思われる場合は、フーバー用語で説明しましょう。
foo
はデコレーター関数、
bar
は装飾する関数、
baz
は装飾された
bar
であることを思い出してください。その場合、 bar は引数を受け取り、
baz
の呼び出し中にそれらを
baz
に渡します。概念を明確にするためのコード例を次に示します。
def foo(bar):
def baz(*args, **kwargs):
# You can do something here
___
# Then we make the call to bar, passing in args and kwargs
bar(*args, **kwargs)
# You can also do something here
___
return baz
*args
と
**kwargs
に見慣れない場合は、これらはそれぞれ、位置引数とキーワード引数への単なるポインタです。
baz
引数にアクセスできるため、
bar
を呼び出す前に引数の検証を実行できることに注意することが重要です。
例としては、デコレータ関数
ensure_string
あり、デコレータ関数に渡される引数が文字列であることを保証する場合が考えられます。次のように実装します。
def ensure_string(func):
def decorated_func(text):
if type(text) is not str:
raise TypeError('argument to ' + func.__name__ + ' must be a string.')
else:
func(text)
return decorated_func
次のように
say_hello
関数を装飾できます。
@ensure_string
def say_hello(name):
print('Hello', name)
次に、これを使用してコードをテストできます。
say_hello('John') # Should run just fine
say_hello(3) # Should throw an exception
そして、次の出力が生成されるはずです。
Hello John
Traceback (most recent call last):
File "/home/anesu/Documents/python-tutorial/./decorators.py", line 20, in <module> say hello(3) # should throw an exception
File "/home/anesu/Documents/python-tu$ ./decorators.pytorial/./decorators.py", line 7, in decorated_func raise TypeError('argument to + func._name_ + must be a string.')
TypeError: argument to say hello must be a string. $0

「John」は文字列であるため、予想どおり、スクリプトは「Hello John」を出力できました。 「Hello 3」を出力しようとすると、「3」が文字列ではないため、例外がスローされました。
ensure_string
デコレータは、文字列を必要とする関数の引数を検証するために使用できます。
クラスの飾りつけ
関数を装飾するだけでなく、クラスを装飾することもできます。デコレータをクラスに追加すると、デコレータ メソッドがクラスのコンストラクタ/イニシエーター メソッド (__init__) を置き換えます。
foo-bar に戻ります。foo がデコレータ、Bar が装飾対象のクラスであるとします。その場合、foo は Bar.__init__ を装飾します。これは、
Bar
型のオブジェクトがインスタンス化される前に何かをしたい場合に便利です。
つまり、次のコードは
def foo(func):
def new_func(*args, **kwargs):
print('Doing some stuff before instantiation')
func(*args, **kwargs)
return new_func
@foo
class Bar:
def __init__(self):
print("In initiator")
と同等です
def foo(func):
def new_func(*args, **kwargs):
print('Doing some stuff before instantiation')
func(*args, **kwargs)
return new_func
class Bar:
def __init__(self):
print("In initiator")
Bar.__init__ = foo(Bar.__init__)
実際、2 つのメソッドのいずれかを使用して定義された Bar クラスのオブジェクトをインスタンス化すると、同じ出力が得られます。
Doing some stuff before instantiation
In initiator

Python のデコレータの例
独自のデコレータを定義できますが、Python にすでに組み込まれているものもあります。 Python で使用される一般的なデコレータの一部を次に示します。
@静的メソッド
静的メソッドは、クラスが装飾しているメソッドが静的メソッドであることを示すためにクラスで使用されます。静的メソッドは、クラスをインスタンス化せずに実行できるメソッドです。次のコード例では、静的メソッド
bark
を使用してクラス
Dog
を作成します。
class Dog:
@staticmethod
def bark():
print('Woof, woof!')
これで、次のように
bark
メソッドにアクセスできるようになります。
Dog.bark()
コードを実行すると、次の出力が生成されます。
Woof, woof!

デコレータの使用方法のセクションで述べたように、デコレータは 2 つの方法で使用できます。
@
構文は、2 つのうちの 1 つとしてより簡潔です。もう 1 つの方法は、デコレーター関数を呼び出し、装飾したい関数を引数として渡します。つまり、上のコードは以下のコードと同じことを実現します。
class Dog:
def bark():
print('Woof, woof!')
Dog.bark = staticmethod(Dog.bark)
そして、同じ方法で
bark
メソッドを使用することもできます。
Dog.bark()
そして、同じ出力が生成されます
Woof, woof!

ご覧のとおり、最初の方法の方がすっきりしており、コードを読み始める前から関数が静的関数であることがより明らかです。そのため、残りの例では最初の方法を使用します。ただし、2 番目の方法は代替手段であることを覚えておいてください。
@クラスメソッド
このデコレータは、デコレータが装飾しているメソッドがクラス メソッドであることを示すために使用されます。クラス メソッドは、呼び出す前にクラスをインスタンス化する必要がないという点で静的メソッドと似ています。
ただし、主な違いは、クラス メソッドはクラス属性にアクセスできるのに対し、静的メソッドはアクセスできないことです。これは、Python が呼び出されるたびに、そのクラスをクラス メソッドの最初の引数として自動的に渡すためです。 Python でクラス メソッドを作成するには、
classmethod
デコレータを使用できます。
class Dog:
@classmethod
def what_are_you(cls):
print("I am a " + cls.__name__ + "!")
コードを実行するには、クラスをインスタンス化せずにメソッドを呼び出すだけです。
Dog.what_are_you()
そして出力は次のようになります。
I am a Dog!

@財産
プロパティ デコレータは、メソッドをプロパティ セッターとしてラベル付けするために使用されます。 Dog の例に戻って、Dog の名前を取得するメソッドを作成しましょう。
class Dog:
# Creating a constructor method that takes in the dog's name
def __init__(self, name):
# Creating a private property name
# The double underscores make the attribute private
self.__name = name
@property
def name(self):
return self.__name
これで、通常のプロパティと同じように犬の名前にアクセスできるようになりました。
# Creating an instance of the class
foo = Dog('foo')
# Accessing the name property
print("The dog's name is:", foo.name)
コードを実行した結果は次のようになります
The dog's name is: foo

@property.setter
property.setter デコレータは、プロパティの setter メソッドを作成するために使用されます。
@property.setter
デコレータを使用するには、
property
セッターを作成するプロパティの名前に置き換えます。たとえば、プロパティ foo のメソッドのセッターを作成している場合、デコレータは
@foo.setter
になります。以下に犬の例を示します。
class Dog:
# Creating a constructor method that takes in the dog's name
def __init__(self, name):
# Creating a private property name
# The double underscores make the attribute private
self.__name = name
@property
def name(self):
return self.__name
# Creating a setter for our name property
@name.setter
def name(self, new_name):
self.__name = new_name
セッターをテストするには、次のコードを使用できます。
# Creating a new dog
foo = Dog('foo')
# Changing the dog's name
foo.name = 'bar'
# Printing the dog's name to the screen
print("The dog's new name is:", foo.name)
コードを実行すると、次の出力が生成されます。
The dogs's new name is: bar

Python におけるデコレータの重要性
デコレータとは何かを説明し、デコレータの例をいくつか見てきました。次に、デコレータが Python で重要である理由について説明します。デコレータはいくつかの理由から重要です。それらのいくつかを以下にリストします。
-
コードの再利用が可能になります。上記の
logging
例では、必要な関数に対して @create_logger を使用できます。これにより、関数ごとに手動でログ機能を作成することなく、すべての関数にログ機能を追加できるようになります。 -
これらを使用すると、モジュラー コードを作成できます。再びロギングの例に戻りますが、デコレータを使用すると、コア関数 (この場合は
say_hello
) を、必要な他の機能 (この場合はロギング) から分離できます。 - フレームワークとライブラリを強化します。デコレータは、追加機能を提供するために Python フレームワークとライブラリで広く使用されています。たとえば、Flask や Django などの Web フレームワークでは、ルートの定義、認証の処理、特定のビューへのミドルウェアの適用にデコレーターが使用されます。
最後の言葉
デコレータは非常に便利です。これらを使用すると、機能を変更せずに機能を拡張できます。これは、関数のパフォーマンスの時間を計測したり、関数が呼び出されるたびにログを記録したり、関数を呼び出す前に引数を検証したり、関数を実行する前に権限を検証したりする場合に便利です。デコレータを理解すると、よりきれいな方法でコードを記述できるようになります。
次に、タプルと Python での cURL の使用に関する記事を読んでください。