Effective Python 读书笔记: 第24条: 以@classmethod形式的多态去通用地构建对象

# -*- encoding: utf-8 -*-

import functools
import os
from threading import Thread

'''
第24条: 以@classmethod形式的多态去通用地构建对象

关键:
1 python类的多态
多态含义: 继承体系的多个类能以自己的方式实现某个方法。
这些类都满足相同的接口但有不同的功能。
局限: python只能有一个__init__构造方法,可以用@classmethod
来初始化类的实例,等同于实现另一种类的__init__方法

2 classmethod
形式:
@classmethod
def fromData(cls, data):
    ......

其中第一个参数是cls,表示类的名称,用于后续创建该类的实例

3 类的多态方法举例
    @classmethod
    def createWorker(cls, inputClass, config):
        workers = []
        # 关键: inputClass.fromData是类级别的多态方法
        for inputData in inputClass.fromData(config):
            workers.append(cls(inputData))
        return workers
分析:
这里在一个类中使用了另一个类的某个方法,我们传入的是类名,
而加入这个类有子类,传入不同的子类,实际是执行不同子类
各自的同名方法,单实现不同的功能,即一个接口,多个功能,
这就是python的多态。

4 总结
1)多态是指一个接口,多种实现。
在C++中可以用类似如下形式实现多态:
ParentClass* pointer = new SubClass1();
......
pointer->commonFunction();
期间可以给point赋予不同的子类指针。
子类指针赋值给父类指针类型,则调用该父类指针时,会根据实际
传入的子类指针而调用对应子类的方法,从而实现多态。

2) 而对于python类的多态实际就是
将类的实例作为参数传给某个方法,在该方法中调用类实例的方法,
如果该类有子类,且每个子类各自都实现了该方法,那么根据传入
的类属于不同的子类,就可以实现该同名方法不同的功能。

3) 通过@classmethod可以给python实现除__init__外的另一个构造方法

参考:
Effectiv Python 编写高质量Python代码的59个有效方法
'''
class BaseInputData(object):
    def read(self):
        raise NotImplementedError

    @classmethod
    def fromData(cls, data):
        raise NotImplementedError


class PathInputData(BaseInputData):
    def __init__(self, path):
        super(PathInputData, self).__init__()
        self.path = path

    def read(self):
        return open(self.path).read()

    @classmethod
    def fromData(cls, config):
        dataDir = config.get('data_dir')
        for name in os.listdir(dataDir):
            yield cls(os.path.join(dataDir, name))


class BaseWorker(object):
    def __init__(self, inputData):
        self.inputData = inputData
        self.result = None

    def map(self):
        raise NotImplementedError

    @classmethod
    def createWorker(cls, inputClass, config):
        workers = []
        '''
        关键: inputClass.fromData是类级别的多态方法
        '''
        for inputData in inputClass.fromData(config):
            workers.append(cls(inputData))
        return workers


class LineCountWorker(BaseWorker):
    def map(self):
        data = self.inputData.read()
        self.result = data.count('\n')

    def reduce(self, other):
        self.result += other.result


def execute(workers):
    threads = [Thread(target=worker.map) for worker in workers ]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()
    first, rest = workers[0], workers[1:]
    for worker in rest:
        first.reduce(worker)
    return first.result

def mapReduce(workerClass, inputClass, config):
    workers = workerClass.createWorker(inputClass, config)
    return execute(workers)

def writeFile(fileName, contentList):
    content = "\n".join(contentList)
    with open(fileName, "w") as fr:
        fr.write(content)

def wirteFiles(fileNames):
    contentList = []
    for index, fileName in enumerate(fileNames):
        content = str(index)
        contentList.append(content)
        writeFile(fileName, contentList)


def getPath(dirPath, fileName):
    result = os.path.join(dirPath, fileName)
    return result


def executeMapReduce():
    dirPath = "./mydir"
    if not os.path.exists(dirPath):
        os.mkdir(dirPath)
    fileNames = [ str(i) + ".txt" for i in range(11)]
    partialGetPath = functools.partial(getPath, dirPath)
    results = map(partialGetPath, fileNames)
    print results
    wirteFiles(results)
    config = {"data_dir": dirPath}
    result = mapReduce(LineCountWorker, PathInputData, config)
    print result
    assert result == 55


def process():
    executeMapReduce()


if __name__ == "__main__":
    process() 

猜你喜欢

转载自blog.csdn.net/qingyuanluofeng/article/details/88914061