Python初心者のための便利なデコレータ解説 #0】そもそもデコレータってなに？仕組みと使い方をやさしく解説！

## はじめに
このシリーズでは、Python初心者の方に向けて、便利なデコレータ機能を実例付きでわかりやすく紹介していきます。

でもその前に...
「そもそもデコレータってなに？」
「なんか難しそう…」
という方のために、今回は **デコレータ** の基本を解説できればと思います！

## デコレータとは？
ざっくり言うと、**デコレータ(decorator)** とは
「関数やクラスに、あとから便利な機能を付け足すしくみ」です。

名前のとおり “decorator” は「装飾するもの」という意味で、すでにある関数やクラスに、まるで“飾りをつける”ように機能を強化できるのがデコレータです。

Python では、関数の上に @〇〇 と書くだけで、その関数に別の処理を追加することができます。


## 🛠️ デコレータはどういうときに使われるの？
```python
def say_hello():
    print("こんにちは！")

say_hello()
```
これは単に「こんにちは」と出力する関数（say_hello）を定義して、実行しているだけのコードです。


✅ 「関数の前後でメッセージを出したい」と思ったら…
```python
def say_hello():
    print("[LOG] 関数を呼び出します")
    print("こんにちは！")
    print("[LOG] 関数の呼び出しが終わりました")

say_hello()
```
print() を使えば、関数の前後にメッセージを出すことは簡単にできます。


でも、`say_hello()` 以外にも `say_goodbye()`, `say_thanks()` など **似たような関数が大量にあったらどうでしょう？**

いちいち書くのは、とても面倒ですよね...
それに、もしログの書式を変更したくなったら、 **すべての関数を修正** しないといけないことになります

🧙‍♂️ **そこで登場するのが「デコレータ」！！**
共通の処理を“あとで外側から”付け足す仕組み、それがデコレータです
次のセクションでは、このログ処理をデコレータでまとめて使いまわせるようにする方法を紹介します！

## ✨ デコレータの使い方
デコレータは、関数の上に @デコレータ名 と書くだけで使えます。
その仕組みを、「関数の前後にログを出すデコレータ」を作ってみながら確認しましょう。

🔧 ステップ1：まずはデコレータを定義する
```python
def log_decorator(func):
    def wrapper():
        print("[LOG] 関数を呼び出します")
        func()
        print("[LOG] 関数の呼び出しが終わりました")
    return wrapper
```
ここでは、log_decorator という関数を定義しています。

```python
@log_decorator
def say_hello():
    print("こんにちは！")

say_hello()
```
デコレートしたい関数の上に`@log_decorator` を記載して、関数を実行すると...

```python
[LOG] 関数を呼び出します  
こんにちは！  
[LOG] 関数の呼び出しが終わりました
```
想定通りの動きになります！

## 🤔 func() と wrapper() って何してるの？

```python
def log_decorator(func):
    def wrapper():
        print("[LOG] 呼び出し前")
        func()
        print("[LOG] 呼び出し後")
    return wrapper
```
デコレータのコードを見て、「`func()`、`wrapper()`ってなに？」「`wrapper()` ってどこで呼ばれてるの？」と思ったかもしれません。

### 🧠 こんなことが起きてます！
```python
def log_decorator(func):
    def wrapper():
        print("[LOG] 関数を呼び出します")
        func()
        print("[LOG] 関数の呼び出しが終わりました")
    return wrapper

    
@log_decorator
def say_hello():
    print("こんにちは！")
```
このように記載した場合、Pythonの内部ではこう書き換えられています

```python
def log_decorator(func):
    def wrapper():
        print("[LOG] 関数を呼び出します")
        func()
        print("[LOG] 関数の呼び出しが終わりました")
    return wrapper

    
def say_hello():
    print("こんにちは！")

say_hello = log_decorator(say_hello)
```
`say_hello = log_decorator(say_hello)` にデコレータの仕組みのすべてが詰まっています。
ここで **「元の関数がラップされて、新しい関数にすり替わる」** という動きが行われているのです。

--- 

### ⚠️ その前に：「関数も変数にできる」って知ってる？

関数も“変数に代入できることを知らないと今回の話は難しいので軽く説明しておきます!

Pythonでは、関数も「値（オブジェクト）」として扱えるんです。

たとえば：
```python
def greet():
    print("こんにちは")

hello = greet  # ← 関数を代入
hello()        # → こんにちは
```
このように、関数名だけで「関数そのもの」を変数に入れて使えるという特徴があります！

---


### 🔍 では、話を戻して `say_hello = log_decorator(say_hello)` を分解して解説します！

✅ ステップ1：say_hello を定義
```python
def say_hello():
    print("こんにちは！")
```
まずは普通に関数が定義されます。
この時点では、say_hello はただ「こんにちは！」と出力する関数です。

✅ ステップ2：`say_hello` を`log_decorator()`に渡す
```python
say_hello = log_decorator(say_hello)
```
ここで、`say_hello` という関数オブジェクトが `log_decorator()` に渡されます。
`log_decorator()` から返される結果が`say_hello`という変数に代入されています。

✅ ステップ3：log_decorator() が wrapper 関数を返す
```python
def log_decorator(func):
    def wrapper():
        print("[LOG] 呼び出し前")
        func()
        print("[LOG] 呼び出し後")
    return wrapper
```
ステップ2で渡した`say_hello` という関数オブジェクトがfuncにが格納されております。
この中で定義された wrapper() は、ログを出してから元の関数（func）を実行し、最後にまたログを出すという関数です。
そして return wrapper によって、このwrapper関数オブジェクトが戻されます。


✅ ステップ4：say_hello が wrapper に上書きされる
```python
say_hello = log_decorator(say_hello)
```
は
```python
say_hello = wrapper
```
と同義になります。

🏁 だから say_hello() を呼ぶと…
これは実際には wrapper() を呼んでいるのと同じ。
その中で func() が呼ばれることで、元の「こんにちは！」の処理もちゃんと実行されるという流れになります。

この仕組みがわかると、なんとなくデコレータの動きが腑に落ちるのではないでしょうか。

## ちなみに：デコレータを2つ以上つけたらどうなる？
Pythonでは、1つの関数にデコレータを複数つけることができます。
このとき、下に書いたデコレータから順に内側に適用されていくのがポイントです。

たとえばこんなコード：
```python
def decorator_a(func):
    def wrapper():
        print("A: before")
        func()
        print("A: after")
    return wrapper


def decorator_b(func):
    def wrapper():
        print("B: before")
        func()
        print("B: after")
    return wrapper


@decorator_a
@decorator_b
def say_hello():
    print("こんにちは！")

say_hello()
```

このとき、Pythonの内部ではこう解釈されます：
```python
say_hello = decorator_a(decorator_b(say_hello))
```
そのため出力は
```
A: before
B: before
こんにちは！
B: after
A: after
```
内側の B が先に実行され、外側の A がその前後をさらに包んでいるイメージです！

## 🧾 まとめ
- デコレータとは、関数やクラスに「あとから機能を追加できる」仕組み
- `@〇〇` の記法で記載する
- 複数のデコレータを使うときは、下に書いたものから順に内側に適用される

## 最後に
次回からはPython標準ライブラリの便利なデコレータを紹介していきたいと思います！