クラスデコレータ
関数のデコレータをクラスで定義
機能を修飾するという意味では、コンテキストマネージャとデコレータは似ています。
多くの場合デコレータは関数を使って定義されますが、クラスで定義することもできます。
コンテキストマネージャのときと同じように、事前に定義したクラスをデコレータとして利用することができます。
関数my_function()のデコレータをクラス定義で実装してみましょう。
code: 0909_class_decorator.py
class MyDecorator:
def my_decorator(self, func):
def decorator_wrapper(*args, **kwargs):
print('before the function called')
func(*args, **kwargs)
print('after the function called')
return decorator_wrapper
decorator = MyDecorator()
@decorator.my_decorator
def my_function(param):
print(f'my_function called with {param}')
my_function(10)
my_function(20)
これを実行すると次のようになります。
code: bash
$ python 0909_class_decorator.py
before the function called
my_function called with 10
after the function called
before the function called
my_function called with 20
after the function called
クラス定義でデコレータを実装していますが、実際のところはメソッドmy_decorator()をデコレータとして使用しているだけのことです。
では、クラスを利用してデコレータを実装する利点はなんでしょうか?
それは、クラスで定義すると、初期化処理や変数を保持できるからです。
次の例は、デコレータで実行された回数をカウントしているものです。
code: 0910_class_decorator_with_count.py
class MyDecorator:
def __init__(self):
self.count = 0
def my_decorator(self, func):
def decorator_wrapper(*args, **kwargs):
print(f'before the function called count:{self.count}')
func(*args, **kwargs)
print('after the function called')
self.count +=1
return decorator_wrapper
decorator = MyDecorator()
@decorator.my_decorator
def my_function(param):
print(f'my_function called with {param}')
my_function(10)
my_function(20)
実行結果は次のとおりで、クラス変数で保持している呼び出し回数が増えていきます。
code: bash
$ python 0910_class_decorator_with_count.py
before the function called count:0
my_function called with 10
after the function called
before the function called count:1
my_function called with 20
after the function called
こんどは、次のように__call__()メソッドを使うとデコレータのような実装ができます。
code: 0911_class_decorator_with_call_method.py
class MyDecorator:
def __init__(self, func):
print('__init__')
self.__func = func
def __call__(self, *args, **kwargs):
print('__call__')
result = self.__func(*args, **kwargs)
return result
@MyDecorator
def my_function(param):
print(f'my_function called with {param}')
my_function(10)
my_function(20)
これを実行すると次の結果となります。
code: bash
$ python 0911_class_decorator_with_call_method.py
__init__
__call__
my_function called with 10
__call__
my_function called with 20
MyDecoratorクラス が 関数my_function に適用されていることがわかります。
デコレータが関数に適用されるとすぐに、クラスの__init__()が呼び出されます。
__init__()は引数として修飾した関数を受け取ります。
そして、my_function()の実行時に、クラスMyDecoratorの__call__()が呼び出されます。
クラスのデコレータ
これまではデコレータで関数を修飾することを説明してきましたが、クラスをデコレータで修飾することもできます。
まずは、単純にクラス継承でアトリビュート familyname を継承させている例です。
code: 0912_class_inheritace_simple.py
# 単純なクラス継承
class Base:
familyname = 'jackson'
class Father(Base):
pass
class Mother(Base):
pass
class Child(Father, Mother):
pass
print(Father.familyname)
print(Mother.familyname)
print(Child.familyname)
これをデコレータでクラスを修飾すると次のように記述できます。
code: 0913_class_decorator_for_class.py
# クラスのデコレータ
def clsdecorator(cls):
class NewClass(cls):
familyname = 'jackson'
return NewClass
@clsdecorator
class Father:
pass
@clsdecorator
class Mother:
pass
class Child(Father, Mother):
pass
print(Father.familyname)
print(Mother.familyname)
print(Child.familyname)