Python中通過@classmethod 實現多態(tài)的示例
通過@classmethod 實現多態(tài)
1.概述
python中通常使用對象創(chuàng)建多態(tài)模式,python還支持類創(chuàng)建多態(tài)模式。下面通過一個例子展示它如何實現多態(tài)。
通過對象創(chuàng)建多態(tài)和類創(chuàng)建多態(tài)開發(fā)模式區(qū)別
- 對象多態(tài)模式:接收的是一個父類類型對象,然后通過傳入父類的子類對象調用普通同名方法,實現不同的行為。
- 類多態(tài)模式:接收的一個父類,然后通過傳入父類的子類調用同名的類方法,實現不通的行為
2.類方法創(chuàng)建多態(tài)模式示例
2.1.普通模式
先通過一個的示例看下常規(guī)方式發(fā)開的代碼沒有使用多態(tài)時候存在的問題,然后在通過類多態(tài)來優(yōu)化代碼。
下面實現一個讀取數據,然后處理數據的示例,他有兩條繼承體系。
輸入信息體系:將輸入信息的方式創(chuàng)建為類繼承關系,可以根據讀取信息的方式創(chuàng)建不同的InputData子類。
處理數據體系:將處理信息的方式創(chuàng)建類繼承關系,根據對信息處理的方式創(chuàng)建不同的Worker子類。
然后通過mapreduce函數組合業(yè)務執(zhí)行邏輯,最后輸出運行結果。
# 接收輸入信息類體系 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 # 組合業(yè)務 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函數通用性不好,不易擴展。例如現在創(chuàng)建了新的InputData、Worker子類,那么就要創(chuàng)建一個新的mapreduce函數來組合新的業(yè)務流程,這樣會導致重復的代碼越來越多,不利于維護。
- python不能向java語言可以在一個類中以重載的形式創(chuàng)建多個構造器,創(chuàng)建一個構造器多態(tài)讓子類可以根據不同的構造器接收不同的參數。而python不能這么做,因為python的類只能有一個構造方法(init),所以沒有辦法為不同的子類提供多種形式的形參構造器,在繼承中也沒有辦法要求所有的子類都只接收只有一種方式參數的構造方法。因為子類要根據自身的特點接收不同類型的參數。
這個問題最好能夠通過類方法多態(tài)來解決,這種多態(tài)與實例方法多態(tài)很像,只不過他針對的是類,而不是這些類的對象。
2.2.類方法多態(tài)重構業(yè)務
類方法多態(tài)的實現非常簡單,下面將代碼中關鍵的點提煉出來。
- 在父類中創(chuàng)建一個通用的方法,不需要實現任何業(yè)務邏輯,然后讓子類中重寫該方法,實現不同的行為。在方法上使用@classmethod裝飾器聲明為類方法,方便調用時候可以以類調用,而不是實例對象調用,通過子類重寫父類的類方法就是類方法多態(tài)。
- 在GenericInputData類中定義一個類方法(generate_inputs)用來解決python只有一個構造方法不能實現多種方式接收參數的弊端,子類通過重寫該方法,實現接收多種參數行為。
class GenericInputData: def read(self): raise NotImplementedError # 創(chuàng)建一個多態(tài)的類方法,讓子類實現不同的功能 @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 # 定義的形參類型為父類,實現了類方法多態(tài) 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')
通過類方法多態(tài)重構代碼后,mapreduce函數支持了多態(tài),它接收的是一個父類類型,根據傳入的實際子類實現把不同的功能。當再擴展GenericInputData、GenericWorker子類后,mapreduce函數不需要修改代碼,實現了左開右閉原則。
到此這篇關于Python中通過@classmethod 實現多態(tài)的文章就介紹到這了,更多相關@classmethod 實現多態(tài)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Python下的XSLT API進行web開發(fā)的簡單教程
這篇文章主要介紹了使用Python下的XSLT API進行web開發(fā)的簡單教程,本文來自于IBM官方網站技術文檔,需要的朋友可以參考下2015-04-04Python虛擬環(huán)境的創(chuàng)建和使用詳解
這篇文章主要給大家介紹了關于Python虛擬環(huán)境的創(chuàng)建和使用的相關資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09