python中通常使用对象创建多态模式,python还支持类创建多态模式。下面通过一个例子展示它如何实现多态。
通过对象创建多态和类创建多态开发模式区别
先通过一个的示例看下常规方式发开的代码没有使用多态时候存在的问题,然后在通过类多态来优化代码。
下面实现一个读取数据,然后处理数据的示例,他有两条继承体系。
输入信息体系:将输入信息的方式创建为类继承关系,可以根据读取信息的方式创建不同的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')
上面接收信息处理信息的示例存在两个问题:
这个问题最好能够通过类方法多态来解决,这种多态与实例方法多态很像,只不过他针对的是类,而不是这些类的对象。
类方法多态的实现非常简单,下面将代码中关键的点提炼出来。
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函数不需要修改代码,实现了左开右闭原则。