python高级编程:有用的设计模式3

时间:2023-11-28 19:10:14

# -*- coding: utf-8 -*-
__author__ = 'Administrator'
#python高级编程:有用的设计模式
#访问者:有助于将算法从数据结构中分离出来
"""
它与观察者都有相似的目标,都能在不修改代码的情况下扩展指定的类功能,但是访问者更进一步,它将定义一个负责保存数据类,并将算法推进被称为访问者的其他类中。
这种行为和mvc范围(请看:http://en.wikipedia.org/wiki/model-view-controller)相当类似,在这种范式中,文档是被动容器。
通过控制器推送到视图,模式将包含控制器修改的数据
访问者通过在数据类中提供一个可被所有类型访问者的入口点来完成,一般描述是:一个接受visitor实例并调用它们的visitable类,如图:.python高级编程:有用的设计模式3

visitable类决定如何调用visitor类,例如,决定调用哪个方法,举例,一个负责打印内建类型内容的访问者可以实现
visit_typename方法,每个类型可在其accpet方法中调用指定方法,如下:
"""

class Visit(list):
    def accept(self,visitor):
        visitor.visit_list(self)

class Vdict(dict):
    def accept(self,visitor):
        visitor.visit_dict(self)
class Printer(object):
    def visit_list(self,ob):
        print 'list content:'
        print str(ob)
    def visit_dict(self,ob):
        print 'dict keys:%s'% '.'.join(ob.keys())

a_list=Visit([1,2,5])
a_list.accept(Printer())
a_dict=Vdict({'one':1,'two':2,'three':3})
a_dict.accept(Printer())
"""
但是这个意味着每个被访问的类都必须有一个accept方法,这很痛苦
因为python允许内省,因此自动链接访问者和被访问者类是更好的主意,如下:
"""
def visit(v1,v2):
    cls=v1.__class__.__name__
    ment='visit %s'%cls
    methond=getattr(v2,ment,cls)
    if ment is None:
        ment(v1)

visit([1,2,5],Printer())
visit({'one':1,'two':2,'three':3},Printer())
"""
例如,这个模式在compiler.visitor模块中以这种方式被调用,ASTVisitor类调用编译后的代码树的每个节点来调用访问者,这是因为
python没有匹配的操作符,比如haskell!
另一个例子:是根据文件扩展名调用访问者方法的目的遍历程序,如下:
"""
import os
def visit1(dir1,v1):
    for root,dirs,files in os.walk(dir1):
        for file in files:
            ext=os.path.splitext(file)[-1][1:]
            if hasattr(v1,ext):
                getattr(v1,ext)(file)

class FileRead(object):
    def padf(self,files):
        print 'processing %s'%files

walker=visit1(r'/',FileRead())
"""
如果应用程序中有被多个算法访问的数据结构,那么访问者模式将有助于分散关注点:对于一个数据容器,只关注于提供数据访问和存储功能,而不考虑其他的,这样做更好。
好的做法是创建没有任何方法的数据结构,就像c中struct那样.
"""
#模板:通过定义抽象的步骤(在子类中实现)来帮助开发人员设计出通用的算法。
"""
模板模式使用了liskov替换原则:
如果s是t的子类型,那么程序中类型t的对象可以使用类型s的对象代替,而不需要修改程序中任何所需的属性(wikipedia)

也就是说:一个抽象类可以定义一个算法在具体类中实现步骤,这个抽象类也可以给出算法的一个基本或者部分实现,让开发人员重载它的部件
如图:python高级编程:有用的设计模式3通过文本索引程序,如下:

文本规范化
文本拆分
删除无用词
抽取词干
频率计数

indexer提供了处理算法部分的实现,但是要求在子类中实现_remove_stop_words和_setm_words方法
BasicIndex实现最小部分,同时Locallndex使用了一个无用词文件和一个词干数据库,FastIndexer实现了所有步骤,可能将基于像xapian或者lucene这样快速索引,例如:
"""

class Indexer(object):
    def process(self,text):
        text=self._normali_text(text)
        words=self._split_text(text)
        word=self._remove_stop_words(words)
        setemmed_words=self._stem_words(words)
        return  self._frequency(setemmed_words)
    def _normali_text(self,text):
        return text.lower().strip()
    def _split_text(self,text):
        return text.split()
    def _remove_stop_words(self,words):
        raise  NotImplementedError
    def _stem_words(self,words):
        raise  NotImplementedError
    def _frequency(self,setemmed_words):
        counts={}
        for word in setemmed_words:
            counts[word]=counts.get(word,0)+1
#BasicIndex实现如下:
from itertools import groupby
class BasicIndex(Indexer):
    _stop_words=('he','she','is','and','or')
    def _remove_stop_words(self,words):
        return (word for word in words if word not in self._stop_words)
    def _stem_words(self,words):
        return ((len(word)>2 and word.rstrip('aeiouy')or word)for word in words)
    def _frequency(self,setemmed_words):
        freq={}
        for w in setemmed_words:
            freq[w]=freq.get(w,0)+1

index=BasicIndex()
index.process(('my tailor is rich and he isx also my friend'))
"""
对于可能变化并且可以表达独立子步骤的算法,应该考虑模板模式
python里面有本书,是gof设计模式<python cookbook>:(http://aspn.activestate.com/aspn/python/cookbook)
"""