在《编写高质量代码:改善python程序的91个建议》的建议53:用状态模式美化代码小节中,介绍了状态模式例如以下:就是当一个对象的内在状态改变时,同意改变其行为,但这个对象看起来像是改变了其类。
    正如:http://blog.csdn.net/ponder008/article/details/6887443博文所写代码,

#encoding=utf-8
#
#by panda
#状态模式

def printInfo(info):
    print unicode(info, 'utf-8').encode('gbk')

#State:上班状态基类
class State():
    def WriteProgram():
        pass

#上午工作状态类
class ForenoonState(State):
    def WriteProgram(self,w):
        if (w.Hour < 12):
            printInfo("当前时间:%d点 工作状态:上午工作,精神百倍" % w.Hour)
        else:
            w.SetState(noonState())
            w.WriteProgram()            

#中午工作状态类
class noonState(State):
    def WriteProgram(self,w):
        if (w.Hour < 13):
            printInfo("当前时间:%d点 午饭。午休" % w.Hour)
        else:
            w.SetState(AfternoonState())
            w.WriteProgram();

#下午工作状态类
class AfternoonState(State):
    def WriteProgram(self,w):
        if (w.Hour < 18):
            printInfo("当前时间:%d点 下午状态还不错,继续努力" % w.Hour)
        else:
            w.SetState(EveningState())
            w.WriteProgram();

#晚上工作状态类
class EveningState(State):
    def WriteProgram(self,w):
        if(w.TaskFinished):
            w.SetState(RestState())
            w.WriteProgram()
            return

        if (w.Hour < 21):
            printInfo("当前时间:%d点 加班哦,好累!

" % w.Hour)

        else:
            w.SetState(SleepingState())
            w.WriteProgram();

#睡眠状态
class SleepingState(State):
    def WriteProgram(self,w):
        printInfo("当前时间:%d点 睡觉了" % w.Hour)

#下班工作状态
class RestState(State):
    def WriteProgram(self,w):
        printInfo("当前时间:%d点 下班回家了" % w.Hour)

#Context:上班
class Work():
    state = ForenoonState();
    TaskFinished = False
    Hour = 8.0

    def SetState(self, state):
        self.state = state

    def WriteProgram(self):
        self.state.WriteProgram(self)

def clientUI():
    work = Work()    
    for i in range(9,23,1):
        work.Hour = i
        if(i > 19):
            work.TaskFinished = True
        work.WriteProgram()
    return

if __name__ == '__main__':
    clientUI();

执行结果:
>>> 
当前时间:9点 工作状态:上午工作,精神百倍
当前时间:10点 工作状态:上午工作,精神百倍
当前时间:11点 工作状态:上午工作,精神百倍
当前时间:12点 午饭。午休
当前时间:13点 下午状态还不错,继续努力
当前时间:14点 下午状态还不错。继续努力
当前时间:15点 下午状态还不错,继续努力
当前时间:16点 下午状态还不错,继续努力
当前时间:17点 下午状态还不错,继续努力
当前时间:18点 加班哦,好累。
当前时间:19点 加班哦。好累。
当前时间:20点 下班回家了
当前时间:21点 下班回家了
当前时间:22点 下班回家了

    这样的状态模式,逻辑控制部分和状态转换控制都放在了不同的状态类中,可是假设我们希望将全部的逻辑控制和状态转换都放在同一个地方,而状态类仅仅须要关注自己要做的事情就可以,就出现了书中的演示样例代码:

def workday():
    print 'work hard!'

def weekday():
    print 'play harder!'

class People(object):pass

people = People()

for i in xrange(1,8):
    if i == 6:
        people.day = weekday
    if i == 1:
        people.day =workday
    people.day()

    解释:当我第一眼看最后一行代码的时候,认为people.day()没定义啊,当我从for開始往下看的时候,才醒悟。汗!

当i=1,day的状态为workday。然后直到i=6才会改变状态为weekday,也就是说,i的值在1~5时。状态一直是workday。到了6才是weekday,当然7也是weekday。

    好了,如今全部的逻辑控制部分都在for里面,两个状态类不用关心状态怎么转换。可是仍然还有下面缺陷(基本摘自书中):
  1. 查询对象的当前状态非常麻烦
  2. 状态切换时假设须要对原状态做一些清理工作,对新的状态做一些初始化工作,那把这个清理和初始化工作都都写在for里面或者原来的状态类里,必定有反复,由于每一个状态都要进行初始化和清理,那我几个状态转换下来,这个for循环已经没法保持好看的身材了。

    我们须要一个机制来简化这个问题。

PS:事实上这些问题仅仅是在状态类较多的情况下更加明显,假设仅仅是两到三个状态类,个人意见是随便写。反复两三条没啥问题(或许是自己要求太低。。)

    好了。言归正传,假设状态类非常多,多到要写状态初始化和清理都非常烦的时候。那我们急需一个辅助工具来做这个反复又头疼的事情,python-state工具通过几个辅助函数和修饰函数攻克了这个问题,并定义了一个简明状态机框架(这个真没看出来,汗!

)。


    地址:https://pypi.python.org/pypi/state/0.1.2dev-r2能够下载。也能够通过pip
install state直接安装。当时看这个包事实上代码量非常少。于是没有安装,直接贴在了代码上面,哈哈。


# -*- coding:utf-8 -*-
import inspect
import functools

class State(object):
    @staticmethod
    def __begin__(host):
        pass

    @staticmethod
    def __end__(host):
        pass

def stateful(cls):
    defaults = []
    for i in cls.__dict__.itervalues():
        if inspect.isclass(i) and issubclass(i, State) and hasattr(i, 'default') and i.default:
            defaults.append(i)
    if not defaults:
        raise Error('%s\'s default state is not found.' % cls.__name__)
    if len(defaults) > 1:
        raise Error('%s\'s has too much default state.%s' % (cls.__name__, defaults))
    default = defaults[0]

    old__init__ = cls.__init__
    if hasattr(cls, '__getattr__'):
        old__getattr__ = getattr(cls, '__getattr__')
    else:
        old__getattr__ = getattr(cls, '__getattribute__')

    def __init__(self, *a, **kw):
        self.__state__ = default
        self.__state__.__begin__(self)
        return old__init__(self, *a, **kw)

    def __getattr__(self, name):
        try:
            old__getattr__(self, name)
        except AttributeError, e:
            pass
        try:
            f = getattr(curr(self), name)
        except AttributeError:
            raise e
        if not callable(f):
            raise e
        return functools.partial(f, self)

    cls.__init__ = __init__
    cls.__getattr__ = __getattr__
    return cls

def curr(host):
    return host.__state__

def switch(host, new_state):
    host.__state__.__end__(host)
    host.__state__ = new_state
    new_state.__begin__(host)

behavior = staticmethod
#上面是state工具的代码,以下是书中的使用演示样例
@stateful
class People(object):
        class Workday(State):
                default = True
                @behavior
                def day(self):
                        print 'word hard!'
        class Weekday(State):
                @behavior
                def day(self):
                        print 'play harder!'
people = People()
for i in xrange(1,8):
        if i == 6:
                switch(people,People.Weekday)
        if i == 1:
                switch(people,People.Workday)
        people.day()

执行下:
[wxx@eb181 worktime]$ ./state_test.py 
word hard!
word hard!
word hard!
word hard!
word hard!
play harder!
play harder!

    依照个人理解解读下这个工具以及使用演示样例。首先State类是让各个状态类继承的,定义了两个静态方法,宿主类(此例为People)重载这两个方法就能够实现初始化和清理工作;接下来是stateful(cls)函数。这是个装饰函数,cls.__dict__.itervalues()列出了全部宿主类的属性,inspect.isclass(i),推断i是否是类,issubclass(i,State)推断是不是State的子类,hasattr(i,'default'),推断是不是默认属性(People中定义default中为True的为默认状态),终于defaults列表中仅仅有Workday状态类,cls.__init__调用People默认的初始化方法,以下一个if
hasattr推断People中是否有__getattr__。显然People类中没有明显的重载__getattr__方法(在People类中有个默认的__getattr__),所以,运行了else部分。初始化部分初始默认状态,完毕默认状态的初始化,即__begin__方法,然后顺便将People类也初始化,核心是重载了__getattr__()方法。查询People类的属性和方法,这里的name值为people类中day。终于cls初始化和__getattr__被重载,返回cls。

这里要注意。People类的day是静态方法的self參数仅仅是为了理解状态类是的宿主是People的实例。

后面的curr方法查询当前状态,switch方法用于切换状态。

(PS:个人感觉getattr那里理解的还有问题。。)

    为了理解getattr方法,懒得自己写,找了网上的代码:
>>> li = "Larry""Curly" ]
>>> li.pop
<built-in method
pop of 
list object at 0xb76b364c>
>>> getattr(
li, 
"pop" )
<built-in method
pop of 
list object at 0xb76b364c>
>>> getattr(
li, 
"append" )( "Moe" )
>>> li
['Larry''Curly''Moe']
>>> 

从上面的代码能够看出li.pop 等用于 getattr( li, "pop" ),可是这样不是调用pop函数。真正的

的调用是getattr( li, "append" )("Moe")。

回到我们的stateful代码,old__getattr__ = getattr(cls,'__getattribute__'),当__getattr__中传来name參数,就相当于运行了cls.__getattribute__.name,假设查找name属性失败,pass这个异常。继续进行getattr(curr(self),name),这里curr(host)返回的是Workday类和Weekday类,name也是day,然后检查f是否是callable的,然后return f的偏函数,传入一个參数self,最后替换cls里的__init__和__getattr__。返回cls。

这个转换状态的方法。首先运行上一个状态的清理工作。也就是__end__,然后指定新的状态。然后完毕初始化。
 
我们看演示样例中的day方法,是个静态方法,为什么有self參数?事实上首先self不是python的keyword,这个self仅仅是为了帮助理解状态类宿主是People的实例。解读完成。

建议53:用状态模式美化代码,关于python-state工具包的理解的更多相关文章

  1. 设计模式19---设计模式之状态模式(State)(行为型)

    1.场景模拟 考虑一个在线投票的应用,分为四种情况 正常投票 正常投票以后还继续重复投票 用户恶意投票 黑名单用户 2.不用模式的解决方案 package demo17.state.example1; ...

  2. Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---状态模式[转]

    {没有应用状态模式的代码} //工程文件 program Project1; {$APPTYPE CONSOLE} uses  uGumballMachine in 'uGumballMachine. ...

  3. 重学 Java 设计模式:实战状态模式「模拟系统营销活动,状态流程审核发布上线场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! @ 目录 一.前言 二.开发环境 三.状态模式介绍 四.案例场景模拟 1 ...

  4. C#设计模式系列:状态模式(State)

    1.状态模式简介 1.1>.定义 状态模式的核心思想是允许一个对象在它的内部状态改变时改变它的行为,即不同的状态对应不同的行为. 状态模式的针对性很强,当有状态变化的时候可以选择状态模式. 1. ...

  5. 深入浅出设计模式——状态模式(State Pattern)

    模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的 ...

  6. iOS开发-状态模式

    状态模式允许对象内部状态改变时改变它的行为,对象看起来好像修改了它的类.状态模式看起来和策略模式比较相像,策略模式是将可以互换的行为封装起来,然后通过使用委托的方式,决定使用哪一个行为,状态也是封装行 ...

  7. 【状态模式】 State Pattern

    我们先设计一个场景,饮料自动售卖机,来设计一下它的出售流程. 流程图中,我们可把这个过程看成几个状态: 投币状态,选择饮料状态,售出状态,出售完毕状态. ,有了这个四个状态,我们设计一下界面(很粗略) ...

  8. Javascript设计模式之我见:状态模式

    大家好!本文介绍状态模式及其在Javascript中的应用. 模式介绍 定义 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是控制一个对象状态的条件表达式 ...

  9. javascript设计模式学习之十六——状态模式

    一.状态模式的定义 状态模式的关键是区分事务内部和外部的状态,事务内部状态改变往往会带来事务的行为改变. 状态模式中有意思的一点是,一般我们谈到封装,都是优先封装对象的行为,而非对象的状态.但在状态模 ...

随机推荐

  1. “Parsing filters unsupported” error during extraction of RAR file

    up vote 159 down vote accepted You can use: sudo apt-get install unrar or sudo apt-get install unar ...

  2. [JavaEE] Spring学习资源链接

    学习规划 http://forrest-lv.iteye.com/blog/1166947 SpringMVC - 框架配置 http://blog.csdn.net/ljhabc1982/artic ...

  3. Hdu-6230 2017CCPC-哈尔滨站 A.Palindrome Manacher 主席树

    题面 题意:给你一个字符串,问你满足s[i]=s[2n-i]=s[2n+i-2]的子串(这子串长度为3n-2)有多少个,原字符串长度<=5e5 题解:对于这种子串,其实要满足2个回文,跑过一次M ...

  4. win7如何给虚拟机设置共享文件

    友情提示:设置之前先把虚拟机关掉 1. 安装vmtools 安装过的,则不需要 重新安装 如果没有安装vmware tools,点击安装(需要联网下载) ,下载完成后,打开虚拟机 点击安装,安装完毕后 ...

  5. Hashmap 详解和迭代器问题

    重点介绍HashMap.首先介绍一下什么是Map.在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value.在下文中会 ...

  6. jar运行main函数的方法

    当把java项目打包成jar后,如何运行main函数呢? 第一种:指定运行类: java -cp test.jar com.ming.test.Test 第二种:在MANIFEST.MF里配置了Mai ...

  7. 第4章 部署模式 Deployment Plan(部署规划)

    已开发了基于组件的应用程序,该应用程序在逻辑上构造为多层结构,如 Three-Layered Services Application. 中所述.您希望将它分布到一组在物理上为多级结构的服务器上,如 ...

  8. Three入门学习笔记整理

    一.官方网站:https://threejs.org 二.关于Three.js 三.开始 四.实例 基本结构 结果 五.概念 坐标系 场景 相机 灯光 3D模型 六.简单动画 七.交互控制 结束 # ...

  9. Educational Codeforces Round 33

    # Who = Penalty * A B C D E F 479 arkethos 4 247   + 00:08 + 00:19 +1 00:59 +2 01:41     479  ne-leo ...

  10. hdu3938 Portal 离线的并查集

    离线算法是将全部输入都读入,计算出所有的答案以后再输出的方法.主要是为避免重复计算.类似于计算斐波那契数列的时候用打表的方法. 题目:给一个无向图,求有多少个点对,使得两点间的路径上的花费小于L,这里 ...