網站首頁 編程語言 正文
通過@classmethod 實現多態
1.概述
python中通常使用對象創建多態模式,python還支持類創建多態模式。下面通過一個例子展示它如何實現多態。
通過對象創建多態和類創建多態開發模式區別
- 對象多態模式:接收的是一個父類類型對象,然后通過傳入父類的子類對象調用普通同名方法,實現不同的行為。
- 類多態模式:接收的一個父類,然后通過傳入父類的子類調用同名的類方法,實現不通的行為
2.類方法創建多態模式示例
2.1.普通模式
先通過一個的示例看下常規方式發開的代碼沒有使用多態時候存在的問題,然后在通過類多態來優化代碼。
下面實現一個讀取數據,然后處理數據的示例,他有兩條繼承體系。
輸入信息體系:將輸入信息的方式創建為類繼承關系,可以根據讀取信息的方式創建不同的InputData子類。
處理數據體系:將處理信息的方式創建類繼承關系,根據對信息處理的方式創建不同的Worker子類。
然后通過mapreduce函數組合業務執行邏輯,最后輸出運行結果。
# 接收輸入信息類體系
class InputData:
def read(self):
raise NotImplementedError
class PathInputData(InputData):
def __init__(self,path):
super().__init__()
self.path = path
def read(self):
with open(self.path) as f:
return f.read()
# 處理信息類體系
class Worker:
def __init__(self, input_data):
self.input_data = input_data
self.result = None
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
class LineCountWorker(Worker):
def map(self):
data = self.input_data
self.result = data.count('/n')
def reduce(self, other):
self.result += other.result
# 組合業務
import os
def generate_inputs(data_dit):
for name in os.listdir(data_dit):
# 讀文件內容
yield PathInputData(os.path.join(data_dit, name))
def create_workers(input_list):
workers = []
for input_data in input_list:
# 處理數據
workers.append(LineCountWorker(input_data))
return workers
from threading import Thread
def execute(workers):
threads = [Thread(target=w.map) for w in workers]
for thread in threads: thread.start()
for thread in threads: thread.join()
first, *rest = workers
for worker in rest:
first.reduce(worker)
return first.result
def mapreduce(data_dir):
inputs = generate_inputs(data_dir)
workers = create_workers(inputs)
return execute(workers)
import os
import random
def write_test_files(tmpdir):
os.makedirs(tmpdir)
for i in range(100):
with open(os.path.join(tmpdir, str(i)), 'w') as f:
f.write('\n' * random.randint(0, 100))
tmpdir = 'test_inputs'
write_test_files(tmpdir)
result = mapreduce(tmpdir)
print(f'There are {result} lines')
上面接收信息處理信息的示例存在兩個問題:
- mapreduce函數通用性不好,不易擴展。例如現在創建了新的InputData、Worker子類,那么就要創建一個新的mapreduce函數來組合新的業務流程,這樣會導致重復的代碼越來越多,不利于維護。
- python不能向java語言可以在一個類中以重載的形式創建多個構造器,創建一個構造器多態讓子類可以根據不同的構造器接收不同的參數。而python不能這么做,因為python的類只能有一個構造方法(init),所以沒有辦法為不同的子類提供多種形式的形參構造器,在繼承中也沒有辦法要求所有的子類都只接收只有一種方式參數的構造方法。因為子類要根據自身的特點接收不同類型的參數。
這個問題最好能夠通過類方法多態來解決,這種多態與實例方法多態很像,只不過他針對的是類,而不是這些類的對象。
2.2.類方法多態重構業務
類方法多態的實現非常簡單,下面將代碼中關鍵的點提煉出來。
- 在父類中創建一個通用的方法,不需要實現任何業務邏輯,然后讓子類中重寫該方法,實現不同的行為。在方法上使用@classmethod裝飾器聲明為類方法,方便調用時候可以以類調用,而不是實例對象調用,通過子類重寫父類的類方法就是類方法多態。
- 在GenericInputData類中定義一個類方法(generate_inputs)用來解決python只有一個構造方法不能實現多種方式接收參數的弊端,子類通過重寫該方法,實現接收多種參數行為。
class GenericInputData:
def read(self):
raise NotImplementedError
# 創建一個多態的類方法,讓子類實現不同的功能
@classmethod
def generate_inputs(cls, config):
raise NotImplementedError
class PathInputData(GenericInputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
with open(self.path) as f:
return f.read()
# 子類重寫父類的類方法
@classmethod
def generate_inputs(cls, config):
data_dir = config['data_dir']
for name in os.listdir(data_dir):
yield cls(os.path.join(data_dir, name))
class GenericWorker:
def __init__(self, input_data):
self.input_data = input_data
self.result = None
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
@classmethod
def create_workers(cls, input_class, config):
workers = []
for input_data in input_class.generate_inputs(config):
workers.append(cls(input_data))
return workers
class LineCountWorker(GenericWorker):
def map(self):
data = self.input_data.read()
self.result = data.count('\n')
def reduce(self, other):
self.result += other.result
# 定義的形參類型為父類,實現了類方法多態
def mapreduce(worker_class, input_class, config):
workers = worker_class.create_workers(input_class, config)
return execute(workers)
config = {'data_dir': tmpdir}
result = mapreduce(LineCountWorker, PathInputData, config)
print(f'There are {result} lines')
通過類方法多態重構代碼后,mapreduce函數支持了多態,它接收的是一個父類類型,根據傳入的實際子類實現把不同的功能。當再擴展GenericInputData、GenericWorker子類后,mapreduce函數不需要修改代碼,實現了左開右閉原則。
原文鏈接:https://blog.csdn.net/m0_38039437/article/details/128050392
相關推薦
- 2022-01-31 RuntimeError:Given input size:(256x1x1). Calculate
- 2022-12-02 C語言中qsort函數用法及用冒泡排序實現_C 語言
- 2023-07-15 express 連接 MongoDb
- 2022-09-29 關于numpy兩個array疊加操作詳解_python
- 2022-06-01 C++的matlab接口轉換方法詳解_C 語言
- 2022-11-19 ubuntu desktop 開啟root賬戶
- 2022-04-12 如何在Python中編寫接口和請求外部接口_python
- 2023-07-27 修改el-button 的樣式
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支