はじめに
この記事は【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"
は関数ではないため、このエラーが起きてます。
さいごに
よくわからない場合は色々実験してみると動きが理解しやすくなるのでおすすめです。