> For the complete documentation index, see [llms.txt](https://docs.xiwind-corp.com/tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xiwind-corp.com/tech/python-library/abc-chou-xiang-class/abc-abcmeta.md).

# ABC, ABCMeta

## ABC

***

Helper class，其 metaclass 為 `ABCMeta`。當一個 Class 繼承自 `ABC` 時，它就成為 Abstract Base Class。

{% code title="PYTHON" %}

```python
abc.ABC
```

{% endcode %}

{% hint style="info" %}
**`ABC` 是包含多個 abstract 方法和 abstract 屬性的 Class，但需要 non-abstract subclass，來提供實現方法和屬性。**&#x20;

這是一種比建立自定義 metaclass 更方便的技術。
{% endhint %}

**Example 1: Basic usage**

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod

class Vehicle(ABC):
    @abstractmethod
    def max_speed(self):
        pass

class Car(Vehicle):
    @property
    def max_speed(self):
        return 200

car = Car()
print(f"Car's max speed: {car.max_speed} km/h") # Output: 200 km/h
```

{% endcode %}

**Example 2: Basic usage**

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

# Trying to instantiate Shape will raise an error
# shape = Shape()

# Working with a concrete implementation
rect = Rectangle(10, 20)
print(f"Rectangle area: {rect.area()}")            # Output: 200
print(f"Rectangle perimeter: {rect.perimeter()}")  # Output: 60
```

{% endcode %}

## ABCMeta

***

定義 Abstract Base Classes (ABCs) 的 metaclass。 Python 中的 metaclass 是 class 的 class，它定義了 class 的行為方式。 `abc.ABCMeta` 提供了在 ABC 中定義 abstract 方法和屬性的機制。

如果不想繼承 `ABC`，可以透過在 Class 中設定 `metaclass=ABCMeta` ，來建立 Abstract Base Class。

{% code title="PYTHON" %}

```python
abc.ABCMeta
```

{% endcode %}

{% hint style="info" %}
**雖然您可以直接使用 `abc.ABCMeta`，但從 `abc.ABC` 繼承通常更簡單且更具可讀性，它抽象化了 metaclasses 的直接使用。**
{% endhint %}

**Example 1: Basic usage**

{% code title="PYTHON" %}

```python
from abc import ABCMeta, abstractmethod

class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass

class Circle(Graphic):
    def draw(self):
        print("Drawing a circle")

# Trying to instantiate Graphic will raise an error
# graphic = Graphic()

# Working with a concrete implementation
circle = Circle()
circle.draw()  # Output: Drawing a circle
```

{% endcode %}

**Example 2: Basic usage**

{% code title="PYTHON" %}

```python
from abc import ABCMeta, abstractmethod, abstractproperty

class Vehicle(metaclass=ABCMeta):
    @abstractproperty
    def max_speed(self):
        pass

class Car(Vehicle):
    @property
    def max_speed(self):
        return 150

car = Car()
print(f"Car's max speed: {car.max_speed} km/h")
```

{% endcode %}

### register()

***

用於將具體 class 註冊為 abstract base class 的 "Virtual subclass"，這意味著即使它沒有繼承自 ABC，但仍能被 `issubclass()` 和 `isinstance()` 等函數識別為 subclass。

{% code title="PYTHON" %}

```python
@ABC.register
```

{% endcode %}

{% code title="PYTHON" %}

```python
ABC.register(Subclass)
```

{% endcode %}

{% hint style="info" %}
**Virtual subclass: 視為另一個 Class 的 subclass，但實際上並沒有繼承自另一個 Class。**
{% endhint %}

{% hint style="info" %}
**`issubclass()` 和 `isinstance()` 會將註冊的 classes，視為 subclasses，即使它們不是從傳統意義上的 ABC 繼承。**
{% endhint %}

{% hint style="info" %}
**盡可能使用 `register()` 作為 decorator，因為它使 ABC 和註冊 class 之間的關係更加明顯。**&#x20;

請務必記錄 `register()` 的使用，因為一個 class 被視為 ABC 的 subclass 的原因，並不總是顯而易見的。
{% endhint %}

**Example 1: Using Decorator**

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod

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

@MyABC.register
class ConcreteClass:
    @property
    def do_something(self):
        return "Doing something in ConcreteClass"

print(issubclass(ConcreteClass, MyABC))  # Output: True
print(issubclass(ConcreteClass, ABC))    # Output: True

x = ConcreteClass()
print(x.do_something)  # Output: Doing something in ConcreteClass
```

{% endcode %}

**Example 2: Using Method Call**

{% code title="PYTHON" %}

```python
from abc import ABC, abstractmethod

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

class AnotherClass:
    @property
    def do_something_else(self):
        return "Doing something in ConcreteClass"

# Registering using method call syntax
MyABC.register(AnotherClass)

# AnotherClass is now considered a subclass of MyABC
print(issubclass(AnotherClass, MyABC))  # Output: True
print(issubclass(AnotherClass, ABC))    # Output: True

x = AnotherClass()
print(x.do_something_else)  # Output: Doing something in ConcreteClass
```

{% endcode %}

**Example 3: Basic usage**&#x20;

當一個 class 繼承自 parent class 時，parent class 會以其方法解析順序 (MRO) 出現，這是 Python 在呼叫方法時，查找方法的順序。

但是，如果一個 class 被註冊為 ABC 的虛擬 subclass，則該 ABC 不會出現在其 MRO 中。這表示 ABC 中定義的方法，不會自動可供虛擬 subclass 使用，而且您不能在虛擬 subclass 中使用 `super()` 來呼叫這些方法。

{% code title="PYTHON" %}

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

# Defining the ABC
class MyAbstractBaseClass(metaclass=ABCMeta):
    @abstractmethod
    def my_method(self):
        pass

# Registering a virtual subclass
class UnrelatedClass:
    def my_method(self):
        print("Implementation in UnrelatedClass")

MyAbstractBaseClass.register(UnrelatedClass)

print(issubclass(UnrelatedClass, MyAbstractBaseClass))  # Outputs: True
print(issubclass(MyAbstractBaseClass, ABC))             # Outputs: False
print(issubclass(MyAbstractBaseClass, ABCMeta))         # Outputs: False

# =========================================================
# Defining the ABC
class MyAbstractBaseClass(ABC):
    @abstractmethod
    def my_method(self):
        pass

# Registering a virtual subclass
class UnrelatedClass:
    def my_method(self):
        print("Implementation in UnrelatedClass")

MyAbstractBaseClass.register(UnrelatedClass)

print(issubclass(UnrelatedClass, MyAbstractBaseClass))  # Outputs: True
print(issubclass(MyAbstractBaseClass, ABC))             # Outputs: True
print(issubclass(MyAbstractBaseClass, ABCMeta))         # Outputs: False
```

{% endcode %}

**Example 4: 影像處理**

{% code title="PYTHON" %}

```python
import cv2
import numpy as np
from abc import ABC, abstractmethod

class ImageProcessor(ABC):
    @abstractmethod
    def process(self, image: np.ndarray) -> np.ndarray:
        """Process an image and return the result."""
        pass


class GrayscaleConverter:
    def process(self, image: np.ndarray) -> np.ndarray:
        return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Registering with the abstract base class
ImageProcessor.register(GrayscaleConverter)


class ImageBlurrer:
    def process(self, image: np.ndarray) -> np.ndarray:
        return cv2.GaussianBlur(image, (5, 5), 0)

# Registering with the abstract base class
ImageProcessor.register(ImageBlurrer)


# Checking if the classes are registered as subclasses
print(issubclass(GrayscaleConverter, ImageProcessor))
print(issubclass(ImageBlurrer, ImageProcessor))

# Example usage
image = cv2.imread('113833397_p0.jpg')
grayscale_converter = GrayscaleConverter()
blurrer = ImageBlurrer()

gray_image = grayscale_converter.process(image)
blurred_image = blurrer.process(image)
```

{% endcode %}

### @abstractmethod

***

當此 decorator 應用於 abstract base class 中的方法時，表示該方法是抽象的。這意味著必須在 ABC 的任何非抽象 subclass 中重寫該方法。

{% code title="PYTHON" %}

```python
@abc.abstractmethod
```

{% endcode %}

{% hint style="success" %}
**`@abc.abstractmethod` 只能影響使用繼承衍生的 subclass； 使用 `register()` 註冊的 Virtual subclass 不受影響。**
{% endhint %}

{% hint style="info" %}
**使用 abstract base classes 和 `@abc.abstractmethod` 主要為一組 subclasses 定義公共 interface。**
{% endhint %}

{% hint style="info" %}
**清楚記錄每個抽象方法應該做什麼，因為這可以作為開發人員實作 subclasses 的指南。**
{% endhint %}

{% hint style="info" %}
**當 `@abc.abstractmethod` 與其他 decorator 結合使用時，應將 `abstractmethod` 作為最內層 decorator。**

例如: `@property`、`@classmethod` 和 `@staticmethod`。
{% endhint %}

**Example 1: Basic usage**

{% code title="PYTHON" %}

```python
import abc
import math

class Shape(abc.ABC):
    @abc.abstractmethod
    def area(self):
        """Method to calculate the area of the shape."""
        pass


class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2


class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width


def print_area(shape):
    print(f"The area of the {shape.__class__.__name__} is {shape.area()}")

# Create instances and use them
circle = Circle(5)
rectangle = Rectangle(4, 6)

print_area(circle)      # Output: The area of the Circle is 78.539
print_area(rectangle)   # Output: The area of the Rectangle is 24
```

{% endcode %}

**Example 2: 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

# Function to demonstrate usage
def test_device(device):
    print(f"Is the device on? {device.is_on}")
    device.turn_on()
    print(f"Is the device on? {device.is_on}")
    device.turn_off()
    print(f"Is the device on? {device.is_on}")

# Create an instance and use it
tv = Television()
test_device(tv)
```

{% endcode %}

**執行結果:**

```txt
Is the device on? No

Television is now ON.
Is the device on? Yes

Television is now OFF.
Is the device on? No
```

**Example 3: 抽象 Properties**

{% code title="PYTHON" %}

```python
import abc

class Vehicle(abc.ABC):
    @property
    @abc.abstractmethod
    def number_of_wheels(self):
        """Abstract property for the number of wheels in a vehicle."""
        pass

class Car(Vehicle):
    @property
    def number_of_wheels(self):
        return 4

class Motorcycle(Vehicle):
    @property
    def number_of_wheels(self):
        return 2

# Testing the implementation
car = Car()
motorcycle = Motorcycle()

print(f"A car has {car.number_of_wheels} wheels.")
print(f"A motorcycle has {motorcycle.number_of_wheels} wheels.")
# Output: A car has 4 wheels..
# Output: A motorcycle has 2 wheels.
```

{% endcode %}

**Example 4: 混合具體方法和抽象方法**

{% code title="PYTHON" %}

```python
import abc

class Computer(abc.ABC):
    @abc.abstractmethod
    def processor(self):
        """Abstract method to define the type of processor."""
        pass

    def boot(self):
        """Concrete method to boot the computer."""
        print(f"Booting up with a {self.processor()} processor.")

class Laptop(Computer):
    def processor(self):
        return "Intel Core i7"

class Server(Computer):
    def processor(self):
        return "AMD Ryzen 9"

# Testing the implementation
laptop = Laptop()
server = Server()

laptop.boot()  # Output: Booting up with a Intel Core i7 processor.
server.boot()  # Output: Booting up with a AMD Ryzen 9 processor.

```

{% endcode %}

**Example 5: 影像處理**

{% code title="PYTHON" %}

```python
import abc
import cv2
import numpy as np

class ImageProcessor(abc.ABC):
    @abc.abstractmethod
    def process(self, image):
        """
        Process an image and return the result.
        """
        pass

    def load_image(self, path):
        """
        Load an image from a given path.
        """
        return cv2.imread(path, cv2.IMREAD_COLOR)

    def save_image(self, image, path):
        """
        Save an image to a given path.
        """
        cv2.imwrite(path, image)


class GaussianBlurProcessor(ImageProcessor):
    def process(self, image):
        """
        Apply Gaussian blur to the image.
        """
        return cv2.GaussianBlur(image, (5, 5), 0)
    

class CannyEdgeProcessor(ImageProcessor):
    def process(self, image):
        """
        Apply Canny Edge Detection to the image.
        """
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(gray, 100, 200)
        return edges


def main():
    input_path = '113833397_p0.jpg'
    output_path_blur = 'output_blur.jpg'
    output_path_edges = 'output_edges.jpg'

    # Initialize processors
    blur_processor = GaussianBlurProcessor()
    edge_processor = CannyEdgeProcessor()

    # Load image
    image = blur_processor.load_image(input_path)

    # Process and save images
    blurred_image = blur_processor.process(image)
    blur_processor.save_image(blurred_image, output_path_blur)

    edge_image = edge_processor.process(image)
    edge_processor.save_image(edge_image, output_path_edges)


if __name__ == "__main__":
    main()
```

{% endcode %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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/abc-abcmeta.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.
