🐍
PythonデコレータのSyntactic Sugarなぜ便利かを理解した
Pythonデコレータを利用する場合、@decorator
というSyntactic Sugarを関数やメソッドの先頭に付けるのが一般的ですが、なぜ便利なのかいまいち理解できていないので、調べてみました。
デコレータの詳細は公式ドキュメントあるいは他の方がすでに紹介されているので、本記事では割愛します。ちなみにおすすめの記事はこちらです。
まず適当に文字列の両側に<b>
を追加してくれる簡単なデコレータを書きましょう。num_b
は片方に追加する<b>
の数を表しており、デフォルトは1となっています。
from functools import partial, wraps
class Emphasis:
def __init__(self) -> None:
pass
def add_b(self, func=None, num_b=1):
if func is None:
return partial(self.add_b, num_b=num_b)
@wraps(func)
def wrap(*args, **kwargs):
ret = func(*args, **kwargs)
return "<b>" * num_b + ret + "<b>" * num_b
return wrap
デコレータ引数なし・関数引数なしの場合
最初は一番簡単なパターンで見ていきましょう。デコレータep.add_b(hello)
の返り値は関数なので、一回コール()
すればSyntactic Sugarと同じことができるので、むしろSyntactic Sugarを使わないほうがわかりすそうですね。
ep = Emphasis()
def hello():
return "Hello, There"
@ep.add_b
def hello_with_sugar():
return "Hello, There"
print(ep.add_b(hello)())
print(hello_with_sugar())
<b>Hello, There<b>
<b>Hello, There<b>
デコレータ引数あり・関数引数ありの場合
しかしデコレータと関数両方引数がある場合はep.add_b(num_b=2)(hello)("Taro")
を書かないといけません。言葉で説明すると、hello
関数を引数としてデコレータep.add_b(num_b=2)
に入れて返された関数に引数"taro"
を渡してコールするという意味です。書けなくはないですが、ややこしくなるので、Syntactic Sugarのほうが断然簡単ですね
ep = Emphasis()
def hello(name):
return f"Hello, {name}"
@ep.add_b(num_b=2)
def hello_with_sugar(name):
return f"Hello, {name}"
print(ep.add_b(num_b=2)(hello)("Taro"))
print(hello_with_sugar("Taro"))
<b><b>Hello, Taro<b><b>
<b><b>Hello, Taro<b><b>
おわりに
Syntactic Sugarのおかげで、デコレータと関数両方引数がある場合でも簡単にデコレータを使えるようになりました。ただ個人的にデコレータを初めて触る方はまずあえてSyntactic Sugarを使わず試してみたら、デコレータの理解が捗るのではないかと思います。
ご参考になれば幸いです。
Discussion