XiWind 西風之劍
HomeTechProContactGitHub
  • About
  • Git
    • Windows Terminal、PowerShell 安裝
    • Git 開始使用
    • Branch 入門
    • 合併多個 Commit , 編輯
    • 額外功能
  • deep learning
    • Dilated Convolution
  • Python
    • GIL 【全域直譯器鎖】
    • PyPy 【JIT 編譯器】
    • Decorator 【修飾器】
      • Class Decorators
  • Python library
    • abc 【抽象 Class】
      • ABC, ABCMeta
      • __abstractmethods__, get_cache_token, update_abstractmethods
    • dataclasses 【數據 Class】
      • make_dataclass(), replace(), is_dataclass(), __post_init__
    • enum 【列舉 Class】
      • Flag, auto(), unique, verify()
      • 範例
    • concurrent.futures 【執行緒、程序】
      • Future, Module Functions
    • queue 【佇列、同步】
      • full(), empty(), qsize(), join(), task_done()
    • functools 【可調用物件】
      • ordering、wrapper、partial
      • Overloading
    • heapq 【堆積佇列】
      • heapify(), merge(), nlargest(), nsmallest()
    • time 【時間】
      • time(), monotonic(), perf_counter()...
      • sleep(), 範例...
    • logging 【日誌】
Powered by GitBook
On this page
  • ABC
  • ABCMeta
  • register()
  • @abstractmethod

Was this helpful?

  1. Python library
  2. abc 【抽象 Class】

ABC


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

PYTHON
abc.ABC

ABC 是包含多個 abstract 方法和 abstract 屬性的 Class,但需要 non-abstract subclass,來提供實現方法和屬性。

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

Example 1: Basic usage

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

Example 2: Basic usage

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

ABCMeta


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

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

PYTHON
abc.ABCMeta

雖然您可以直接使用 abc.ABCMeta,但從 abc.ABC 繼承通常更簡單且更具可讀性,它抽象化了 metaclasses 的直接使用。

Example 1: Basic usage

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

Example 2: Basic usage

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")

register()


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

PYTHON
@ABC.register
PYTHON
ABC.register(Subclass)

Virtual subclass: 視為另一個 Class 的 subclass,但實際上並沒有繼承自另一個 Class。

issubclass() 和 isinstance() 會將註冊的 classes,視為 subclasses,即使它們不是從傳統意義上的 ABC 繼承。

盡可能使用 register() 作為 decorator,因為它使 ABC 和註冊 class 之間的關係更加明顯。

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

Example 1: Using Decorator

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

Example 2: Using Method Call

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

Example 3: Basic usage

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

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

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

Example 4: 影像處理

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)

@abstractmethod


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

PYTHON
@abc.abstractmethod

@abc.abstractmethod 只能影響使用繼承衍生的 subclass; 使用 register() 註冊的 Virtual subclass 不受影響。

使用 abstract base classes 和 @abc.abstractmethod 主要為一組 subclasses 定義公共 interface。

清楚記錄每個抽象方法應該做什麼,因為這可以作為開發人員實作 subclasses 的指南。

當 @abc.abstractmethod 與其他 decorator 結合使用時,應將 abstractmethod 作為最內層 decorator。

例如: @property、@classmethod 和 @staticmethod。

Example 1: Basic usage

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

Example 2: Basic usage

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)

執行結果:

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

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.

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

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.

Example 5: 影像處理

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()

Previousabc 【抽象 Class】Next__abstractmethods__, get_cache_token, update_abstractmethods

Last updated 1 year ago

Was this helpful?

Page cover image