I'm a python addict !

J. Cliff Dyer jcd at sdf.lonestar.org
Mon Jan 26 15:06:18 EST 2009


On Mon, 2009-01-26 at 09:52 -0800, Paul McGuire wrote:
> On Jan 26, 10:54 am, "J. Cliff Dyer" <j... at sdf.lonestar.org> wrote:
> > On Fri, 2009-01-23 at 20:25 -0800, Paul McGuire wrote:
> > > Want to change the type/behavior of an object from class A to class
> > > B?  How about this:
> >
> > >     aobj = A()
> > >     aobj.__class__ = B
> >
> > > Try *that* in as simple-looking C++ or Java!
> >
> > Wow.  That looks very powerful and fun.  But scary.  Any thoughts on how
> > you would use that in a way that wouldn't unleash sulphurous code
> > smells?
> >
> 
> This technique is perfect for implementing the GoF State pattern.
> 
> In the State pattern, you implement behavior for an object's various
> states using one of several interchangeable classes.  The classes are
> "interchangeable" in that they all implement a common interface.  Here
> is my favorite State pattern example, a traffic light:
> 
> 
> import time
> 
> class TrafficLight(object):
>     pass
> 
> class RedLight(TrafficLight):
>     cars_can_go = False
>     pedestrians_can_cross = True
>     color = (255,0,0)
>     duration = 20
> 
> class YellowLight(TrafficLight):
>     cars_can_go = True
>     pedestrians_can_cross = False
>     color = (255,255,0)
>     duration = 5
> 
> class GreenLight(TrafficLight):
>     cars_can_go = True
>     pedestrians_can_cross = False
>     color = (0,255,0)
>     duration = 15
> 
> # now add in next_state class vars for state transitions
> RedLight.next_state = GreenLight
> YellowLight.next_state = RedLight
> GreenLight.next_state = YellowLight
> TrafficLight.initial_state = RedLight
> 
> # run a traffic light for a while...
> can_they = lambda cond : ("can't","can")[cond]
> light = TrafficLight.initial_state()
> while 1:
>     print light.__class__.__name__
>     print "waiting for", light.duration, "seconds"
>     print "Cars", can_they(light.cars_can_go), "go"
>     print "People", can_they(light.pedestrians_can_cross), "cross"
>     print
>     time.sleep(light.duration)
> 
>     # how you have to do it in C++ and Java
>     # light = light.next_state()
> 
>     # using Python
>     light.__class__ = light.next_state
> 
> 
> Gives this output:
> 
> RedLight
> waiting for 20 seconds
> Cars can't go
> People can cross
> 
> GreenLight
> waiting for 15 seconds
> Cars can go
> People can't cross
> 
> YellowLight
> waiting for 5 seconds
> Cars can't go
> People can't cross
> 
> RedLight
> waiting for 20 seconds
> Cars can't go
> People can cross
> 
> ... and so on ...
> 
> 
> 
> In Python, the base TrafficLight class isn't even necessary ("don't
> need no stinking interfaces!"), although it is a good place to define
> default behavior, and it helps tie together the other classes from a
> self-documentation standpoint.  But any class that has the necessary
> attributes would suffice, whether it inherits from TrafficLight or
> not.
> 
> class HoldForEmergencyVehiclesLight(object):
>     cars_can_go = False
>     pedestrians_can_cross = False
>     color = (255,0,0)
> 
> 
> -- Paul
> 

Thanks.  That makes sense.  But your example creates a new instance of
the new class each time, rather than changing the class of a persistent
instance, as the original example, to which I was responding, did.

But perhaps something like:

class TrafficLight(object):
    def change_light(self):
        self.__class__ = next_state

and then you can persist information about the light (bulb_type in
set(['led', 'incandescent']), last_maintenance_date, location, etc.) on
the instance level, unaffected by the changing color.

Interesting stuff.

Cheers,
Cliff





More information about the Python-list mailing list