建议53:用状态模式美化代码小节中,介绍了状态模式如下:就是当 一个对象的内在状态改变时,允许改变其行为,但这个对象看起来像是改变了其类。


    正如: 博文所写代码,


#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__': 
 
 
 
;
 
 
 
  
 
 

   运行结果:


>>> 



当前时间: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循环已经没法保持好看的身材了。我们需要一个机制来简化这个问题。


    好了,言归正传,如果状态类很多,多到要写状态初始化和清理都很烦的时候,那我们急需一个辅助工具来做这个重复又头疼的事情,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-inmethod pop of listobjectat 0xb76b364c> 
      >>> getattr( li, "pop") 
      <built-inmethod pop of listobjectat 0xb76b364c> 
      >>> getattr( li, "append")( "Moe") 
      >>> li 
      ['Larry', 'Curly', 'Moe'] 
      >>> 



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

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




这个转换状态的方法,首先执行上一个状态的清理工作,也就是__end__,然后指定新的状态,然后完成初始化。


 




我们看示例中的day方法,是个静态方法,为什么有self参数?其实首先self不是python的关键字,这个self只是为了帮助理解状态类宿主是People的实例。解读完毕。