from abc import ABC, abstractmethodclassVehicle(ABC):@abstractmethoddefmax_speed(self):passclassCar(Vehicle):@propertydefmax_speed(self):return200car =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, abstractmethodclassShape(ABC):@abstractmethoddefarea(self):pass@abstractmethoddefperimeter(self):passclassRectangle(Shape):def__init__(self,width,height): self.width = width self.height = heightdefarea(self):return self.width * self.heightdefperimeter(self):return2* (self.width + self.height)# Trying to instantiate Shape will raise an error# shape = Shape()# Working with a concrete implementationrect =Rectangle(10, 20)print(f"Rectangle area: {rect.area()}")# Output: 200print(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。
from abc import ABCMeta, abstractmethodclassGraphic(metaclass=ABCMeta):@abstractmethoddefdraw(self):passclassCircle(Graphic):defdraw(self):print("Drawing a circle")# Trying to instantiate Graphic will raise an error# graphic = Graphic()# Working with a concrete implementationcircle =Circle()circle.draw()# Output: Drawing a circle
Example 2: Basic usage
PYTHON
from abc import ABCMeta, abstractmethod, abstractpropertyclassVehicle(metaclass=ABCMeta):@abstractpropertydefmax_speed(self):passclassCar(Vehicle):@propertydefmax_speed(self):return150car =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。
盡可能使用 register() 作為 decorator,因為它使 ABC 和註冊 class 之間的關係更加明顯。
請務必記錄 register() 的使用,因為一個 class 被視為 ABC 的 subclass 的原因,並不總是顯而易見的。
Example 1: Using Decorator
PYTHON
from abc import ABC, abstractmethodclassMyABC(ABC):@abstractmethoddefdo_something(self):pass@MyABC.registerclassConcreteClass:@propertydefdo_something(self):return"Doing something in ConcreteClass"print(issubclass(ConcreteClass, MyABC))# Output: Trueprint(issubclass(ConcreteClass, ABC))# Output: Truex =ConcreteClass()print(x.do_something)# Output: Doing something in ConcreteClass
Example 2: Using Method Call
PYTHON
from abc import ABC, abstractmethodclassMyABC(ABC):@abstractmethoddefdo_something(self):passclassAnotherClass:@propertydefdo_something_else(self):return"Doing something in ConcreteClass"# Registering using method call syntaxMyABC.register(AnotherClass)# AnotherClass is now considered a subclass of MyABCprint(issubclass(AnotherClass, MyABC))# Output: Trueprint(issubclass(AnotherClass, ABC))# Output: Truex =AnotherClass()print(x.do_something_else)# Output: Doing something in ConcreteClass
Example 3: Basic usage
當一個 class 繼承自 parent class 時,parent class 會以其方法解析順序 (MRO) 出現,這是 Python 在呼叫方法時,查找方法的順序。
from abc import ABC, ABCMeta, abstractmethod# Defining the ABCclassMyAbstractBaseClass(metaclass=ABCMeta):@abstractmethoddefmy_method(self):pass# Registering a virtual subclassclassUnrelatedClass:defmy_method(self):print("Implementation in UnrelatedClass")MyAbstractBaseClass.register(UnrelatedClass)print(issubclass(UnrelatedClass, MyAbstractBaseClass))# Outputs: Trueprint(issubclass(MyAbstractBaseClass, ABC))# Outputs: Falseprint(issubclass(MyAbstractBaseClass, ABCMeta))# Outputs: False# =========================================================# Defining the ABCclassMyAbstractBaseClass(ABC):@abstractmethoddefmy_method(self):pass# Registering a virtual subclassclassUnrelatedClass:defmy_method(self):print("Implementation in UnrelatedClass")MyAbstractBaseClass.register(UnrelatedClass)print(issubclass(UnrelatedClass, MyAbstractBaseClass))# Outputs: Trueprint(issubclass(MyAbstractBaseClass, ABC))# Outputs: Trueprint(issubclass(MyAbstractBaseClass, ABCMeta))# Outputs: False
Example 4: 影像處理
PYTHON
import cv2import numpy as npfrom abc import ABC, abstractmethodclassImageProcessor(ABC):@abstractmethoddefprocess(self,image: np.ndarray) -> np.ndarray:"""Process an image and return the result."""passclassGrayscaleConverter:defprocess(self,image: np.ndarray) -> np.ndarray:return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# Registering with the abstract base classImageProcessor.register(GrayscaleConverter)classImageBlurrer:defprocess(self,image: np.ndarray) -> np.ndarray:return cv2.GaussianBlur(image, (5, 5), 0)# Registering with the abstract base classImageProcessor.register(ImageBlurrer)# Checking if the classes are registered as subclassesprint(issubclass(GrayscaleConverter, ImageProcessor))print(issubclass(ImageBlurrer, ImageProcessor))# Example usageimage = 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 中重寫該方法。
import abcimport mathclassShape(abc.ABC):@abc.abstractmethoddefarea(self):"""Method to calculate the area of the shape."""passclassCircle(Shape):def__init__(self,radius): self.radius = radiusdefarea(self):return math.pi * self.radius **2classRectangle(Shape):def__init__(self,length,width): self.length = length self.width = widthdefarea(self):return self.length * self.widthdefprint_area(shape):print(f"The area of the {shape.__class__.__name__} is {shape.area()}")# Create instances and use themcircle =Circle(5)rectangle =Rectangle(4, 6)print_area(circle)# Output: The area of the Circle is 78.539print_area(rectangle)# Output: The area of the Rectangle is 24
Example 2: Basic usage
PYTHON
import abcclassElectronicDevice(abc.ABC):@abc.abstractmethoddefturn_on(self):pass@abc.abstractmethoddefturn_off(self):pass@property@abc.abstractmethoddefis_on(self):passclassTelevision(ElectronicDevice):def__init__(self): self._is_on =Falsedefturn_on(self):ifnot self._is_on: self._is_on =Trueprint("Television is now ON.")defturn_off(self):if self._is_on: self._is_on =Falseprint("Television is now OFF.")@propertydefis_on(self):return self._is_on# Function to demonstrate usagedeftest_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 ittv =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 abcclassVehicle(abc.ABC):@property@abc.abstractmethoddefnumber_of_wheels(self):"""Abstract property for the number of wheels in a vehicle."""passclassCar(Vehicle):@propertydefnumber_of_wheels(self):return4classMotorcycle(Vehicle):@propertydefnumber_of_wheels(self):return2# Testing the implementationcar =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 abcclassComputer(abc.ABC):@abc.abstractmethoddefprocessor(self):"""Abstract method to define the type of processor."""passdefboot(self):"""Concrete method to boot the computer."""print(f"Booting up with a {self.processor()} processor.")classLaptop(Computer):defprocessor(self):return"Intel Core i7"classServer(Computer):defprocessor(self):return"AMD Ryzen 9"# Testing the implementationlaptop =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 abcimport cv2import numpy as npclassImageProcessor(abc.ABC):@abc.abstractmethoddefprocess(self,image):""" Process an image and return the result. """passdefload_image(self,path):""" Load an image from a given path. """return cv2.imread(path, cv2.IMREAD_COLOR)defsave_image(self,image,path):""" Save an image to a given path. """ cv2.imwrite(path, image)classGaussianBlurProcessor(ImageProcessor):defprocess(self,image):""" Apply Gaussian blur to the image. """return cv2.GaussianBlur(image, (5, 5), 0)classCannyEdgeProcessor(ImageProcessor):defprocess(self,image):""" Apply Canny Edge Detection to the image. """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 100, 200)return edgesdefmain(): 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()