Welcome, guest | Sign In | My Account | Store | Cart

Decorator for use with objects following the state pattern.

Python, 87 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def statemethod(method):
    def call_statemethod(self, *args, **kwargs):
        # Use self.state.<method> if available, else method itself.
        real_method = getattr(self.state, method.func_name, method)
        return real_method(self, *args, **kwargs)
    call_statemethod.default = method
    return call_statemethod


# Sample usage:
class State(object):
    """Base State class, direct parent to non-instantiated states.

       Useful when you have lots of base objects and don't need to store
       per-state data."""

    @classmethod
    def new(cls):
        """Create a new Base object with this as the initial state."""
        return Base(cls.get_state())

    @classmethod
    def get_state(cls):
        """Get the state, for use with an existing Base object"""
        return cls

class InstantiatedState(State):
    """InstantiatedState creates a new object every time get_state is called.

       This allows for independant per-state data storage by multiple base
       objects."""

    @classmethod
    def get_state(cls):
        """Get a state object, for use with an existing Base object"""
        return cls()

class Base(object):
    def __init__(self, initial_state):
        self.state = initial_state

    def ordinary_method(self):
        print "This method is ordinary."

    @statemethod
    def default_method(self):
        print "This is a default method that has not been overridden."

    @statemethod
    def overridden_method(self):
        print "You shouldn't see this."
        assert False

class SimpleState(State):
    @staticmethod
    def overridden_method(base):
        print "The method on %r has been overridden by SimpleState." % base

class DataState(InstantiatedState):
    message = "Awesome."
    def overridden_method(self, base):
        print "This method on %r has been overridden by DataState.  %s" \
                          % (base,                            self.message)

print "Base A"
print "======"
base_a = SimpleState.new()
print "Calling default_method:"
base_a.default_method()
print "Calling overridden_method:"
base_a.overridden_method()
print "Switching to DataState."
base_a.state = DataState.get_state()
print "Calling overridden_method:"
base_a.overridden_method()
print "Changing message."
base_a.state.message = "Excellent."
print "Calling overridden_method:"
base_a.overridden_method()
print
print "Base B"
print "======"
base_b = DataState.new()
print "Calling default_method:"
base_b.default_method()
print "Calling overridden_method:"
base_b.overridden_method()

The state pattern solves the problem of having one object that, depending on its current state, can act in several different ways. Conceptually, this might be seen as a parent class with several subclasses that the object switches between.

The decorator makes it easy to place a function under the state's control. The original method acts as a default, but if the current state has a method with the same name, it's used instead.

To call the default method after overriding it, use:

base.<method>.default(...)

This is analogous to a super() call in a subclass:

super(Subclass, self).<method>(...)
Created by Charlie Nolan on Mon, 15 Jun 2009 (MIT)
Python recipes (4591)
Charlie Nolan's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks