经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
Python中通过@classmethod 实现多态的示例
来源:jb51  时间:2022/11/28 8:55:57  对本文有异议

通过@classmethod 实现多态

1.概述

python中通常使用对象创建多态模式,python还支持类创建多态模式。下面通过一个例子展示它如何实现多态。

通过对象创建多态和类创建多态开发模式区别

  • 对象多态模式:接收的是一个父类类型对象,然后通过传入父类的子类对象调用普通同名方法,实现不同的行为。
  • 类多态模式:接收的一个父类,然后通过传入父类的子类调用同名的类方法,实现不通的行为

2.类方法创建多态模式示例

2.1.普通模式

先通过一个的示例看下常规方式发开的代码没有使用多态时候存在的问题,然后在通过类多态来优化代码。

下面实现一个读取数据,然后处理数据的示例,他有两条继承体系。
输入信息体系:将输入信息的方式创建为类继承关系,可以根据读取信息的方式创建不同的InputData子类。
处理数据体系:将处理信息的方式创建类继承关系,根据对信息处理的方式创建不同的Worker子类。

然后通过mapreduce函数组合业务执行逻辑,最后输出运行结果。

  1. # 接收输入信息类体系
  2. class InputData:
  3. def read(self):
  4. raise NotImplementedError
  5.  
  6. class PathInputData(InputData):
  7. def __init__(self,path):
  8. super().__init__()
  9. self.path = path
  10.  
  11. def read(self):
  12. with open(self.path) as f:
  13. return f.read()
  14.  
  15. # 处理信息类体系
  16. class Worker:
  17. def __init__(self, input_data):
  18. self.input_data = input_data
  19. self.result = None
  20.  
  21. def map(self):
  22. raise NotImplementedError
  23.  
  24. def reduce(self, other):
  25. raise NotImplementedError
  26.  
  27. class LineCountWorker(Worker):
  28. def map(self):
  29. data = self.input_data
  30. self.result = data.count('/n')
  31.  
  32. def reduce(self, other):
  33. self.result += other.result
  34.  
  35. # 组合业务
  36. import os
  37. def generate_inputs(data_dit):
  38. for name in os.listdir(data_dit):
  39. # 读文件内容
  40. yield PathInputData(os.path.join(data_dit, name))
  41.  
  42. def create_workers(input_list):
  43. workers = []
  44. for input_data in input_list:
  45. # 处理数据
  46. workers.append(LineCountWorker(input_data))
  47. return workers
  48.  
  49. from threading import Thread
  50. def execute(workers):
  51. threads = [Thread(target=w.map) for w in workers]
  52. for thread in threads: thread.start()
  53. for thread in threads: thread.join()
  54.  
  55. first, *rest = workers
  56. for worker in rest:
  57. first.reduce(worker)
  58. return first.result
  59.  
  60. def mapreduce(data_dir):
  61. inputs = generate_inputs(data_dir)
  62. workers = create_workers(inputs)
  63. return execute(workers)
  64.  
  65. import os
  66. import random
  67. def write_test_files(tmpdir):
  68. os.makedirs(tmpdir)
  69. for i in range(100):
  70. with open(os.path.join(tmpdir, str(i)), 'w') as f:
  71. f.write('\n' * random.randint(0, 100))
  72. tmpdir = 'test_inputs'
  73. write_test_files(tmpdir)
  74. result = mapreduce(tmpdir)
  75. print(f'There are {result} lines')

上面接收信息处理信息的示例存在两个问题:

  • mapreduce函数通用性不好,不易扩展。例如现在创建了新的InputData、Worker子类,那么就要创建一个新的mapreduce函数来组合新的业务流程,这样会导致重复的代码越来越多,不利于维护。
  • python不能向java语言可以在一个类中以重载的形式创建多个构造器,创建一个构造器多态让子类可以根据不同的构造器接收不同的参数。而python不能这么做,因为python的类只能有一个构造方法(init),所以没有办法为不同的子类提供多种形式的形参构造器,在继承中也没有办法要求所有的子类都只接收只有一种方式参数的构造方法。因为子类要根据自身的特点接收不同类型的参数。

这个问题最好能够通过类方法多态来解决,这种多态与实例方法多态很像,只不过他针对的是,而不是这些类的对象

2.2.类方法多态重构业务

类方法多态的实现非常简单,下面将代码中关键的点提炼出来。

  • 在父类中创建一个通用的方法,不需要实现任何业务逻辑,然后让子类中重写该方法,实现不同的行为。在方法上使用@classmethod装饰器声明为类方法,方便调用时候可以以类调用,而不是实例对象调用,通过子类重写父类的类方法就是类方法多态。
  • 在GenericInputData类中定义一个类方法(generate_inputs)用来解决python只有一个构造方法不能实现多种方式接收参数的弊端,子类通过重写该方法,实现接收多种参数行为。
  1. class GenericInputData:
  2. def read(self):
  3. raise NotImplementedError
  4. # 创建一个多态的类方法,让子类实现不同的功能
  5. @classmethod
  6. def generate_inputs(cls, config):
  7. raise NotImplementedError
  8.  
  9. class PathInputData(GenericInputData):
  10. def __init__(self, path):
  11. super().__init__()
  12. self.path = path
  13.  
  14. def read(self):
  15. with open(self.path) as f:
  16. return f.read()
  17. # 子类重写父类的类方法
  18. @classmethod
  19. def generate_inputs(cls, config):
  20. data_dir = config['data_dir']
  21. for name in os.listdir(data_dir):
  22. yield cls(os.path.join(data_dir, name))
  23.  
  24.  
  25. class GenericWorker:
  26. def __init__(self, input_data):
  27. self.input_data = input_data
  28. self.result = None
  29.  
  30. def map(self):
  31. raise NotImplementedError
  32.  
  33. def reduce(self, other):
  34. raise NotImplementedError
  35.  
  36. @classmethod
  37. def create_workers(cls, input_class, config):
  38. workers = []
  39. for input_data in input_class.generate_inputs(config):
  40. workers.append(cls(input_data))
  41. return workers
  42.  
  43. class LineCountWorker(GenericWorker):
  44. def map(self):
  45. data = self.input_data.read()
  46. self.result = data.count('\n')
  47.  
  48. def reduce(self, other):
  49. self.result += other.result
  50.  
  51. # 定义的形参类型为父类,实现了类方法多态
  52. def mapreduce(worker_class, input_class, config):
  53. workers = worker_class.create_workers(input_class, config)
  54. return execute(workers)
  55. config = {'data_dir': tmpdir}
  56. result = mapreduce(LineCountWorker, PathInputData, config)
  57. print(f'There are {result} lines')

通过类方法多态重构代码后,mapreduce函数支持了多态,它接收的是一个父类类型,根据传入的实际子类实现把不同的功能。当再扩展GenericInputData、GenericWorker子类后,mapreduce函数不需要修改代码,实现了左开右闭原则。

到此这篇关于Python中通过@classmethod 实现多态的文章就介绍到这了,更多相关@classmethod 实现多态内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号