24. 使用@classmethod构造对象

原文标题:USE @CLASSMETHOD POLYMORPHISM TO CONSTRUCT OBJECTS GENERICALLY

在Python中不止对象(object)支持多态性,类(class)也支持多态性。多态性可以使一个层级中的多个类对同一个方法有不同的实现。这允许许多类为相同的接口或抽象基类提供不同的实现。

例如,你正在实现MapReduce,想用一个通用类来表示输入数据。下面我定义了一个类,这个类有一个read方法,这是一个抽象方法,只能在子类中实现:

24. 使用@classmethod构造对象

下面是InputData类的字类,可以从文件中读取数据:

24. 使用@classmethod构造对象

像PathInputData 一样,你可以实现很多InputData 的子类,这些子类都实现了read方法并且返回bytes数据,这些子类可以从网络、压缩包读取数据。

接下来编写一个MapReduce类,这个类定义了一个简单的抽象接口可以使用一种标准的方式接收输入数据:

24. 使用@classmethod构造对象

下面定义一个Worker的实体类,这个类可以计算文件中有几个新行:

24. 使用@classmethod构造对象

这种代码实现看起来不错,但是曾经却给我带来了很多严重问题。我们要如何才能将这么多的代码片段关联起来?我在前面定义的类每一个都清晰,但是怎么样才能很好的为MapReduce服务呢?

最简单的方式就是手动实例化输入数据,然后通过一个帮助方法把数据于worker关联起来。下面的代码首先列举出一个目录中的所有内容然后为每一个文件创建一个PathInputData实例:

24. 使用@classmethod构造对象

接下来创建一个LineCountWorker 实例,这个实例可以使用上面方法返回的InputData数据实例:

24. 使用@classmethod构造对象

下面通过多线程的方式执行worker:

24. 使用@classmethod构造对象

最后编写一个mapreduce方法把所有代码串起来:

24. 使用@classmethod构造对象

以上代码用于测试一组文件时非常方便。例如测试临时目录下所有文件一个有多少行:

24. 使用@classmethod构造对象

但是你发现有什么问题了吗?这个问题就是mapreduce方法并不通用。如果你想要读取其它类型的数据源,你就需要重写generate_inputs,create_workers,并且还要重写mapreduce 以便能够适应新的代码。

如果使用其它编程语言的话,就可以通过构造函数的多态性来实现对不同数据源的支持,但是Python只支持一个构造函数,这就是__init__。我们很难要求每一个InputData的子类都有一个兼容性的构造函数。

最好的方式就是使用@classmethod来实现多态。除了应用于类外,这与前面的实例方法read是一样的。

下面在MapReduce方法中应用一下这个想法。下面扩展一下InputData类,给这个类增加一个通用方法用于生成数据。:

24. 使用@classmethod构造对象

generate_inputs 方法可以接收一个字典,这个字典值取决于InputData实例。重写PathInputData:

24. 使用@classmethod构造对象

相应的更新GenericWorker 类,create_workers 接收的input_class必须是GenericInputData的子类。下面我在构造GenericWorker 的实体子类时使用cls()做为构造器:

24. 使用@classmethod构造对象

上面input_class.generate_inputs就是多态性的实现。在这里你还能看到除了__init__以外的另一种构建对象实例的方法:cls方法。

到目前为止,我们只需要修改LineCountWorker的基类就可以了:

24. 使用@classmethod构造对象

最后重写mapreduce方法:

24. 使用@classmethod构造对象

调用mapreduce时还需要提供额外的一些参数:

24. 使用@classmethod构造对象

接下来你就可以通过编写新的GenericInputData 和GenericWorker 来实现对不同数据源的支持了。


分享到:


相關文章: