はじめに
この記事は【python】今度こそ理解できるデコレータの動き①引数を受け取らないデコレータ
の続きです。
この記事のゴールは以下の形のデコレータを理解することです。
def deco(a):
# 処理
@deco(a="hello")
def test():
# 処理
最小限のコードで引数を受け取るデコレータの動きを確認していきます。
最初に@の後ろの関数が実行される
def foo(b):
pass
def deco(a):
print(a)
return foo
@deco(a="hello")
def test():
pass
# 実行結果
# hello
1行ずつ見ていきます。
1~6行目は関数を定義しているだけなので何も起きません。
8行目の@deco(a="hello")に到達すると、はじめに deco(a="hello")が実行されます。
4~6行目関数decoが実行され、aに"hello"が渡されると文字列helloが出力されます。
そして関数fooを返します。
頭の中は↓のような感じです。
@deco(a="hello")の deco(a="hello")が実行されて関数fooが返るから、
deco(a="hello")はfooと置き換わり、8行目は@fooになった。
ここまでをまとめると、
- コードが
@deco(引数)に到達すると、deco(引数)がまず実行される
※これ以降は【python】今度こそ理解できるデコレータの動き①引数を受け取らないデコレータ
のステップ1~3と全く同じです。
なので以降ざっと動きをみます。細かい動きは↑を読んでみてください。
以降は引数を受け取らないデコレータと全く同じ
def foo(func):
print(func.__name__)
return func
def deco(a):
print(a)
return foo
@deco(a="hello")
def test():
print("inside test func")
pass
test()
# 出力
# hello
# test
8行目はステップ1と同様にdeco(a="hello")が実行され、helloが出力された後に関数fooが返されます。
この時点で@deco(a="hello")を頭の中で@fooに置き換えます。
fooはfuncの__name__属性を出力して、funcを返す関数です。
@deco(a="hello")を頭の中で@fooに置き換えると、@の後ろには関数fooがあります。
@の後ろの関数(この場合foo)の第一引数には@の次の行の関数が渡されます。
よって、イメージ的には、
-
@deco(a="hello")の時点でdeco(a="hello")が実行される。 -
deco(a="hello")が実行されることでhelloが出力され、関数fooが返る - これ以降
@deco(a="hello")を@fooとして考えられる -
@fooの直後の関数testが定義される - 関数
testが関数fooに引数として渡されてfooが実行される - 変数
testにfooの返り値(=関数test)が代入される - test()を実行すると
inside test funcが出力される
のような感じです。
ポイントは、関数decoは関数(callable)を返さないといけないということです。
なぜなら関数を返さないと呼び出すことができないのでエラーが起きるからです。
例)deco関数のreturn文を return 123とかにすると、上のイメージの5番で123(test)のようにint型の123を呼び出そうとしてエラーになります。
実験・エラー集(色々いじると理解が進みます)
decoが関数(callable)を返さない場合
def deco(a):
print(a)
return 123
@deco(a="hello")
def test():
print("inside test func")
pass
# 結果
# hello
# Traceback (most recent call last):
# File "test.py", line 5, in <module>
# @deco(a="hello")
# TypeError: 'int' object is not callable
イメージ的には以下のような感じです。
-
deco(a="hello")が実行され、123が返される -
@deco(a="hello")を頭の中で@123に書き換える -
@123の直後にdef test():があるので、関数testが定義される -
123(test)が実行され、'int' object is not callableが出力される。これは123は関数でないので、呼び出すことができないからです。
decoが返す関数の返り値が関数(callable)でない場合
def foo(func):
print(func.__name__)
return "aaa"
def deco(a):
print(a)
return foo
@deco(a="hello")
def test():
print("inside test func")
pass
test()
# 結果
# hello
# test
# Traceback (most recent call last):
# File "test.py", line 17, in <module>
# test()
# TypeError: 'str' object is not callable
イメージは以下のような感じです。
-
deco(a="hello")が実行され、関数fooが返される -
@deco(a="hello")を頭の中で@fooに置き換える -
@fooの後ろで関数testが定義される - 関数
fooに関数testが渡される - 関数
fooが文字列"aaa"を返す - 変数
testに文字列"aaa"が格納される -
test()は"aaa"()を行っているので、'str' object is not callableが出力される。"aaa"は関数ではないため、このエラーが起きてます。
さいごに
よくわからない場合は色々実験してみると動きが理解しやすくなるのでおすすめです。