# Class Decorators

## Class Decorators

***

Decorators 是強大的工具，允許我們修改函數或 Class 的行為。 Decorators 可以某種方式修改 Class。這通常涉及返回一個新 Class，該新 Class 用一些修改或增強的功能替換原始 Class。

{% code title="PYTHON" %}

```python
def add_attribute(cls):
    cls.new_attribute = "New attribute added by decorator"
    return cls

@add_attribute
class MyClass():
    pass

# Areate an instance of MyClass
obj = MyClass()

# Access the new attribute
print(obj.new_attribute)  # Output: New attribute added by decorator
```

{% endcode %}

下面是一範例，它將任何調用的方法的結果乘以 10。 在這種情況下，`double_results` decorator 會檢查 Class 的所有方法，如果找到任一個可調用方法，它將用 `double_decorator` 函數包裝它，使結果值加倍。

{% code title="PYTHON" %}

```python
def double_results(cls):
    # iterate over all attributes
    for attr_name, attr_value in cls.__dict__.items():
        if callable(attr_value):  # if the attribute is a method
            setattr(cls, attr_name, double_decorator(attr_value))
    return cls

def double_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs) * 10
    return wrapper

@double_results
class MyClass:
    def add(self, a, b):
        return a + b

# create an instance of MyClass
obj = MyClass()

# call the 'add' method
print(obj.add(2, 2))  # Output: 40
```

{% endcode %}

{% hint style="info" %}
**Class decorators 是一個更高層次的概念，不僅需要了解 Classes 如何工作，還需要了解 decorators 如何工作。**
{% endhint %}

{% hint style="info" %}
**Decorators允許輕鬆修改類和函數，而無需更改其原始碼。 這遵循物件導向編程中的** [**Open–closed principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) **。(物件應該對擴展開放，但對修改應該關閉)**
{% endhint %}

{% hint style="success" %}
**當您有需要應用於多個 Class 的修改時，使用 Class decorators 是最有意義的。 如果修改特定的單個 Class，則直接修改該 Class 可能會更簡單、更清晰。**
{% endhint %}

## Decorator Class

***

Python 中的 decorator 是用於修改函數、方法或 Class 的任何可調用物件。 可調用物件是任何可以被調用的物件 (具有 `__call__` 方法的函數或 Class)。

使用 Class 實現的 decorator 可以提供更多的靈活性和對裝飾應用方式的控制。

**簡單的 decorator 來追蹤函數被調用的次數：**

{% code title="PYTHON" %}

```python
class CallCount:
    def __init__(self, f):
        self.f = f
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"This function is called {self.count} times")
        return self.f(*args, **kwargs)

@CallCount
def hello(name):
    print(f"Hello, {name}")

```

{% endcode %}

**執行結果:**

```txt
This function is called 1 times
Hello, Python
```

**Decorator class 用於計時函數執行：**

{% code title="PYTHON" %}

```python
import time

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start_time = time.time()
        result = self.func(*args, **kwargs)
        end_time = time.time()
        print(f"Executing {self.func.__name__} took {end_time - start_time:4f} s")
        return result

@Timer
def complex_calculation(n):
    return sum(i*i for i in range(n))

print(complex_calculation(10000))
```

{% endcode %}

**執行結果:**

```txt
Executing complex_calculation took 0.001001 s
333283335000
```

### 範例 – 帶參數的 Decorator Class

***

要創建帶有參數的 Class-based decorator，我們需要確保 Class 本身接受這些參數。

{% code title="PYTHON" %}

```python
class Repeat:
    def __init__(self, num_times):
        self.num_times = num_times

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for _ in range(self.num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper

@Repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

greet("Alice")
```

{% endcode %}

**執行結果:**

```txt
Hello Alice
Hello Alice
Hello Alice
```

### 範例 – Decorator Class 實例

***

{% code title="PYTHON" %}

```python
class DecoratorClass:
    def __init__(self, value):
        self.value = value

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f"Before function call, value is: {self.value}")
            result = func(*args, **kwargs)
            print(f"After function call, value is: {self.value}")
            return result
        return wrapper

    def increment(self):
        self.value += 1

decorator = DecoratorClass(10)
decorator.increment()

@decorator
def test_function():
    print("Inside the function")

test_function()
```

{% endcode %}

**執行結果:**

```txt
Hello Alice
Hello Alice
Hello Alice
```

## PEP 614

***

PEP 代表 Python Enhancement Proposal (增強提案)，它是一個描述 Python 新功能和改進的標準。 這些 PEP 在被接受並在 Python 中實現之前，經過了仔細的審查和討論過程。

PEP 614 (Python 3.9 ver.) 消除了 decorators 語法的限制，允許任何有效的表達式充當裝飾器；允許更具表現力和更複雜的裝飾器。

## 參考資料

***

[decorator — Python 3.11.4 documentation](https://docs.python.org/3/glossary.html#term-decorator)

[PEP 318 – Decorators for Functions and Methods | peps.python.org](https://peps.python.org/pep-0318/)

[PEP 3129 – 類裝飾器 | peps.python.org](https://peps.python.org/pep-3129/)

[PEP 614 – 放寬對裝飾器的語法限制 | peps.python.org](https://peps.python.org/pep-0614/)

[PEP 698 – 覆蓋靜態類型裝飾器 | peps.python.org](https://peps.python.org/pep-0698/)

[Python進階技巧 (3) — 神奇又美好的 Decorator ，嗷嗚！ | by Jack Cheng | 整個程式都是我的咖啡館 | Medium](https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-3-%E7%A5%9E%E5%A5%87%E5%8F%88%E7%BE%8E%E5%A5%BD%E7%9A%84-decorator-%E5%97%B7%E5%97%9A-6559edc87bc0)

[\[Python教學\] 裝飾詞原理到應用 | Max行銷誌](https://www.maxlist.xyz/2019/12/07/python-decorator/)

[Python Decorator 入門教學](https://blog.techbridge.cc/2018/06/15/python-decorator-%E5%85%A5%E9%96%80%E6%95%99%E5%AD%B8/)

[Python 有哪些好玩的語法糖？ - 知乎](https://www.zhihu.com/question/57470958)

[Python語法糖系列\_上帝De助手的博客-CSDN博客\_語法糖](https://blog.csdn.net/five3/article/details/83474633)

[詳解Python修飾器(語法糖)\_咆哮的阿傑的博客-CSDN博客\_修飾器](https://blog.csdn.net/qq_34914551/article/details/107358768)

[Python 中甜甜的語法糖 - 騰訊雲開發者社區-騰訊雲](https://cloud.tencent.com/developer/article/1671829)

[Python 中甜甜的語法糖\_牛客博客](https://blog.nowcoder.net/n/a8d75227c51f42609375281d7205abd4)

[python中裝飾器&語法糖(syntax sugar)@\_Mr\_ggx的博客-CSDN博客\_python怎樣達到和語法糖同樣的效果](https://blog.csdn.net/guanguoxiang/article/details/46500813)

[Python 函數裝飾器 | 菜鳥教程](https://www.runoob.com/w3cnote/python-func-decorators.html)

[裝飾器 - 廖雪峰的官方網站](https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584)

[Python 中的裝飾器 - GeeksforGeeks](https://www.geeksforgeeks.org/decorators-in-python/)

[Python 中帶參數的裝飾器 - GeeksforGeeks](https://www.geeksforgeeks.org/decorators-with-parameters-in-python/)

[collections - 如何在 python 中使用 kwargs 作為裝飾器的參數\_python\_酷徒編程知識庫](https://stackoverflow.com/questions/70043312/how-to-use-kwargs-as-parameter-for-decorator-in-python)

[Python 中帶參數的裝飾器 - GeeksforGeeks](https://www.geeksforgeeks.org/decorators-with-parameters-in-python/)

[DAY09-搞懂Python的裝飾器 - iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天](https://ithelp.ithome.com.tw/articles/10200763)

[collections - 如何在 python 中使用 kwargs 作為裝飾器的參數\_python\_酷徒編程知識庫](https://stackoverflow.com/questions/70043312/how-to-use-kwargs-as-parameter-for-decorator-in-python)

[Python 中帶參數的裝飾器 - GeeksforGeeks](https://www.geeksforgeeks.org/decorators-with-parameters-in-python/)
