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