# \_\_abstractmethods\_\_, get\_cache\_token, update\_abstractmethods

## \_\_abstractmethods\_\_

***

是一個維護系統的屬性，包含必須由 abstract base class 的具體子類別實現的抽象方法和屬性的名稱。

當使用 `abc` module 定義 abstract base class 時，任何用 `@abstractmethod` 修飾的方法都會加入到 `__abstractmethods__` set 中。除非覆寫其所有抽象方法，否則無法實例化從 abstract base class 繼承的類別。

**Example: Basic usage**

{% code title="PYTHON" %}

```python
import abc

class ElectronicDevice(abc.ABC):
    @abc.abstractmethod
    def turn_on(self):
        pass

    @abc.abstractmethod
    def turn_off(self):
        pass

    @property
    @abc.abstractmethod
    def is_on(self):
        pass


class Television(ElectronicDevice):
    def __init__(self):
        self._is_on = False

    def turn_on(self):
        if not self._is_on:
            self._is_on = True
            print("Television is now ON.")

    def turn_off(self):
        if self._is_on:
            self._is_on = False
            print("Television is now OFF.")

    @property
    def is_on(self):
        return self._is_on
    
print(ElectronicDevice.__abstractmethods__) # Output: frozenset({'turn_on', 'turn_off', 'is_on'})
print(Television.__abstractmethods__)       # Output: frozenset()
```

{% endcode %}

## get\_cache\_token()

***

提供了一個 token，每次透過 `register()` 修改ABC時，該 token 都會更改。此 token 對於 caching 和追蹤 ABC registry 的變更很有用。

它返回一個不透明的物件 (意味著用戶無法直接理解或使用其內部結構)，用於標記 Virtual subclass cache 的版本。 該物件支援相等性測試，因此可以用來檢查 cache 是否已經更改。

{% code title="PYTHON" %}

```python
abc.get_cache_token()
```

{% endcode %}

{% hint style="info" %}
**每次在調用 `register()` 時，cache token 都會發生變化。 這意味著當您註冊 Virtual subclass cache 時，cache token 將會更改。**&#x20;

Cache token 是一種版本控制的機制。
{% endhint %}

{% hint style="warning" %}
**它主要用於處理大型 Class 層次結構，並且需要確保正確傳遞 Abstract Class 中的更改的情況。大多數 Python 開發人員可能永遠不需要使用 `get_cache_token()`。**
{% endhint %}

**Example: Basic usage**

{% code title="" %}

```python
import abc
from abc import ABC

class MyABC(ABC):
    pass

cache_token = abc.get_cache_token()
print(cache_token)  # Output: 37

# Function to demonstrate cache token usage
def is_abc_up_to_date(cache_token):
    current_token = abc.get_cache_token()
    return  current_token == cache_token

print("ABC registry updated:", not is_abc_up_to_date(cache_token)) # Output: False

@MyABC.register
class MyConcreteClass:
    pass
print("ABC registry updated:", not is_abc_up_to_date(cache_token)) # Output: True
```

{% endcode %}

## update\_abstractmethods()

***

用於重新計算 Abstract class 的抽象狀態，在建立 Class 後加入或實現 abstract 方法的情況下非常有用。

如果沒有使用 `update_abstractmethods(cls)`，Python 不會自動重新計算的 abstract 方法。

{% code title="PYTHON" %}

```python
abc.update_abstractmethods(cls)
```

{% endcode %}

{% hint style="info" %}
**在 Abstract class 已經定義之後，您不能動態地加入新的 abstract 方法。 僅能使用 `update_abstractmethods()` 。**
{% endhint %}

{% hint style="info" %}
**`update_abstractmethods()` 並不常用。用例涉及動態行為，這在傳統的 Python 應用程式中並不常見。它更常見於進行大量 meta-programming 的 rameworks or libraries 中。**
{% endhint %}

{% hint style="warning" %}
**頻繁使用此函數會使程式碼更難以理解和維護。通常最好對 class 層次結構有一個清晰、靜態的定義。**
{% endhint %}

**Example: Basic usage**

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod, update_abstractmethods

class AbstractParent(ABC): 
    @abstractmethod
    def do_something(self): 
        pass


class ChildClass(AbstractParent): 
    pass


try: 
    c = ChildClass()  # ❌❌
except TypeError as e: 
    print(e)  
    
def do_something_implementation(self): 
    print("Doing something!")

# Recompute abstract state
ChildClass.do_something = do_something_implementation
update_abstractmethods(ChildClass)

c = ChildClass()      # ✔️✔️
c.do_something()      # Output:  Doing something!
```

{% endcode %}

## 範例 – 計算面積

***

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod

class Polygon(ABC):
    def __init__(self, sides):
        super().__init__()
        self.sides = sides

    @abstractmethod
    def area(self):
        """Verification of side length"""
        if not all(i > 0 for i in self.sides):
            raise ValueError("All side lengths must be positive")

class Rectangle(Polygon):
    def __init__(self, sides):
        super().__init__(sides)

    def area(self):
        super().area()
        # Calculate rectangle area
        return self.sides[0] * self.sides[1]

class Triangle(Polygon):
    def __init__(self, sides):
        super().__init__(sides)

    def area(self):
        super().area()
        # Calculate Heron's formula
        s = sum(self.sides) / 2
        return (s * (s - self.sides[0]) * (s - self.sides[1]) * (s - self.sides[2])) ** 0.5

print(Rectangle([5, 10]).area())      # Output: 50
print(Triangle([20, 10, 15]).area())  # Output: 72.618

```

{% endcode %}

### 範例 – 影像處理

***

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod
from PIL import Image

class ImageProcessor(ABC):
    def __init__(self, image_path):
        super().__init__()
        self.image = Image.open(image_path)

    @abstractmethod
    def process(self):
        if self.image.mode != 'RGB':
            raise ValueError("Image mode must be RGB")

class ImageResizer(ImageProcessor):
    def __init__(self, image_path, size):
        super().__init__(image_path)
        self.size = size

    def process(self):
        super().process() # Resize the image
        return self.image.resize(self.size)

class ImageRotator(ImageProcessor):
    def __init__(self, image_path, angle):
        super().__init__(image_path)
        self.angle = angle

    def process(self):
        super().process() # Rotate the image
        return self.image.rotate(self.angle)

resizer = ImageResizer('image.jpg', (800, 800))
resized_image = resizer.process()
resized_image.save('resized_image.jpg')

rotator = ImageRotator('image.jpg', 90)
rotated_image = rotator.process()
rotated_image.save('rotated_image.jpg')
```

{% endcode %}

## 參考資料

***

[abc — Abstract Base Classes — Python 3.11.4 documentation](https://docs.python.org/3/library/abc.html)

[PEP 3119 – 引入抽象基類 | peps.python.org](https://peps.python.org/pep-3119/)

[Python 中的抽象基類 (abc) - GeeksforGeeks](https://www.geeksforgeeks.org/abstract-base-class-abc-in-python/)

[Python interfaces: abandon ABC and switch to Protocols | by Oleg Sinavski | Level Up Coding](https://levelup.gitconnected.com/python-interfaces-choose-protocols-over-abc-3982e112342e)

[python抽象類別 abc模組 - 知乎](https://zhuanlan.zhihu.com/p/508700685)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.xiwind-corp.com/tech/python-library/abc-chou-xiang-class/__abstractmethods__-get_cache_token-update_abstractmethods.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
