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

You want to implement stateful objects, which have a different set of behaviours according to what state they are in.

This requirement can be achieved with the use of mix-ins. A mix-in is a class which is dynamically inherited by an object. The methods of the mix-in class are thus accessible through the object. This is a clean way of providing objects with standard interfaces.

Chuck Esterbrook has written a discussion of mix-ins, and their implementation in Python, which can be found here: http://www.linuxjournal.com/article.php?sid=4540

The recipe draws from the famous "Lumberjack Song". If you're not familiar with it, all you need to know is that the passage of time modifies the Lumberjack's behaviour.

Python, 157 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
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# ! /usr/bin/env python
# Lumberjack.py
# Author: D. Haynes
# 9th July 2002
# 
# For more details on mix-ins, see
# http://www.linuxjournal.com/article.php?sid=4540

class CambridgeMan:
    def roots(self):
        return "But I'm forever a Cambridge Man"

class LumberjackInterface:
    def __init__(self, methods):
        self.methods = list(methods)

    def __iter__(self):
        return self

    def next(self):
        try:
            return self.methods.pop(0)
        except IndexError:
            raise StopIteration

class LumberjackBase(CambridgeMan):
    def __init__(self, name):
        self.bases = self.__class__.__bases__
        self.name = name
        self.title = "Young %s" % self.name

    def _mixIn(self, mixInClass):
        self.__class__.__bases__ = (mixInClass,) + self.bases
        mixInClass.__init__(self, self.name)

    def Interface(self):
        """
            Return a list of the names of the methods to be used by
            the client.

        """
        unpublishedMethods = ("roots", "Interface",
        "WishIdBeenAGirlie")
        methodType = type(self.Interface)
        ifList = []
        for i in dir(self):
            if (type(getattr(self, i)) == methodType
            and not i.startswith('_')):
                ifList.append(i)
        for i in unpublishedMethods:
            ifList.remove(i)
        return LumberjackInterface(ifList)

class RookieLumberjackMixIn:
    """
        I cut down trees, I eat my lunch, I go to the lavatory.
        On Wednesdays I go shopping, and have buttered scones for
        tea...

    """

    def __init__(self, name):
        self.name = name
        self.title = "Young %s" % self.name

    def CutDownTrees(self):
        return "%s cut down a tree." % self.name

    def EatMyLunch(self):
        return "%s eats his lunch." % self.name

    def GoShopping(self):
        return "%s has gone shopping." % self.name

    def WishIdBeenAGirlie(self):
        return 0


class SeasonedLumberjackMixIn:
    """
        I cut down trees, I skip and jump, I like to press wild
        flowers. I put on womens' clothing, and hang around in bars...

    """

    def __init__(self, name):
        self.name = name
        self.title = "Head Lumberjack %s" % name

    def CutDownTrees(self):
        return "%s cut down a tree." % self.name

    def SkipAndJump(self):
        return "%s skips and jumps." % self.name

    def HangAroundInBars(self):
        return "%s is in the bar." % self.name

    def WishIdBeenAGirlie(self):
        return 0

class VeteranLumberjackMixIn:
    """
        I cut down trees, I wear high heels, suspenders and a bra.
        I wish I'd been a girlie, just like my dear Papa...

    """
    def __init__(self, name):
        self.name = name
        self.title = "Old Man %s" % name

    def CutDownTrees(self):
        return "%s cut down a tree." % self.name

    def WearHighHeels(self):
        return "%s has stilletos on." % self.name

    def WishIdBeenAGirlie(self):
        return 1

class LumberjackFactory:
    def __init__(self):
        pass

    def Lumberjack(self, name):
        jack = LumberjackBase(name)
        jack._mixIn(RookieLumberjackMixIn)
        return jack

def display(jack):
    print "I'm %s and I can..." % jack.title
    for i in jack.Interface():
        print i
    print "And I do%s wish I was a girlie!" % ("n't" * (1 - jack.
    WishIdBeenAGirlie()))
    print

def do(jack):
    for i in jack.Interface():
        print apply(getattr(jack, i))
    print

if __name__ == "__main__":
    factory = LumberjackFactory()
    jack = factory.Lumberjack("Jack")
    display(jack)
    do(jack)

    jack._mixIn(SeasonedLumberjackMixIn)
    display(jack)
    do(jack)

    jack._mixIn(VeteranLumberjackMixIn)
    display(jack)
    do(jack)

    print jack.roots()

Core object functionality is provided in the LumberjackBase class. This class may inherit from others in the usual way. However, since we will be manipulating the inheritance of this class, one of the first things a LumberjackBase object does is to store a record of its base classes. The class contains one compulsory method, _mixIn, of which more later. There is also an Interface method in this example, which serves to publish the methods currently available via a simple object which could be pickled and passed via a Queue to other threads or processes.

A factory object is used to assemble a new Lumberjack object from the LumberjackBase class. State is given to this object by mixing in a class which provides its initial functional interface. The mix-in mechanism is achieved using the _mixin method of the LumberjackBase object.

The _mixin method swaps the current mixed-in class for a new one, retaining the core inherited classes. Finally, the __init__ method of the new class is called to perform any mix-in specific initialisation.

The example shows how versatile this approach can be; it allows the stateful objects to inherit from other classes, to overlap state functionality, or to hide methods from the client. One application of this might be in implementing the "Domain Model" pattern to inform a GUI what operations are possible on a selected item.

1 comment

Dave Haynes (author) 21 years, 5 months ago  # | flag

Engineers don't know their footwear. Of course, that should be 'stiletto'. But you get the point :-D

Created by Dave Haynes on Sat, 2 Nov 2002 (PSF)
Python recipes (4591)
Dave Haynes's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks