【python】今度こそ理解できるデコレータの動き②引数を受け取るデコレータ

# はじめに
この記事は[【python】今度こそ理解できるデコレータの動き①引数を受け取らないデコレータ
](https://qiita.com/namtok/items/4c53edf365d646209a27)の続きです。

この記事のゴールは以下の形のデコレータを理解することです。

```py
def deco(a):
    # 処理

@deco(a="hello")
def test():
    # 処理
```

最小限のコードで**引数を受け取るデコレータ**の動きを確認していきます。

# 最初に@の後ろの関数が実行される
```py
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】今度こそ理解できるデコレータの動き①引数を受け取らないデコレータ
](https://qiita.com/namtok/items/4c53edf365d646209a27)の**ステップ1~3と全く同じ**です。
なので以降ざっと動きをみます。細かい動きは↑を読んでみてください。

# 以降は引数を受け取らないデコレータと全く同じ

```py
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`)の第一引数には`@`の次の行の関数が渡されます。

よって、イメージ的には、

1. `@deco(a="hello")`の時点で`deco(a="hello")`が実行される。
1. `deco(a="hello")`が実行されることで `hello`が出力され、関数`foo`が返る
1. これ以降`@deco(a="hello")`を`@foo`として考えられる
1. `@foo`の直後の関数`test`が定義される
1. 関数`test`が関数`foo`に引数として渡されて`foo`が実行される
1. 変数`test`に`foo`の返り値(=関数`test`)が代入される
1. test()を実行すると`inside test func`が出力される

のような感じです。

ポイントは、**関数`deco`は関数(callable)を返さないといけない**ということです。
なぜなら関数を返さないと呼び出すことができないのでエラーが起きるからです。
例）deco関数のreturn文を `return 123`とかにすると、上のイメージの5番で`123(test)`のように`int`型の123を呼び出そうとしてエラーになります。


# 実験・エラー集(色々いじると理解が進みます)

### decoが関数(callable)を返さない場合

```py
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
```
イメージ的には以下のような感じです。

1. `deco(a="hello")`が実行され、`123`が返される
2. `@deco(a="hello")`を頭の中で`@123`に書き換える
3. `@123`の直後に`def test():`があるので、関数`test`が定義される
4. `123(test)`が実行され、`'int' object is not callable`が出力される。これは`123`は関数でないので、呼び出すことができないからです。

### decoが返す関数の返り値が関数(callable)でない場合

```py
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
```

イメージは以下のような感じです。
1. `deco(a="hello")`が実行され、関数`foo`が返される
2. `@deco(a="hello")`を頭の中で`@foo`に置き換える
3. `@foo`の後ろで関数`test`が定義される
4. 関数`foo`に関数`test`が渡される
5. 関数`foo`が文字列`"aaa"`を返す
6. 変数`test`に文字列`"aaa"`が格納される
7. `test()`は`"aaa"()`を行っているので、`'str' object is not callable`が出力される。`"aaa"`は関数ではないため、このエラーが起きてます。


# さいごに
よくわからない場合は色々実験してみると動きが理解しやすくなるのでおすすめです。