How to write Strategy Pattern in Python differently than example in Wikipedia? How to write Strategy Pattern in Python differently than example in Wikipedia? python python

How to write Strategy Pattern in Python differently than example in Wikipedia?


The example in Python is not so different of the others. To mock the PHP script:

class StrategyExample:    def __init__(self, func=None):        if func:             self.execute = func    def execute(self):        print("Original execution")def executeReplacement1():    print("Strategy 1")def executeReplacement2():    print("Strategy 2")if __name__ == "__main__":    strat0 = StrategyExample()    strat1 = StrategyExample(executeReplacement1)    strat2 = StrategyExample(executeReplacement2)    strat0.execute()    strat1.execute()    strat2.execute()

Output:

Original executionStrategy 1Strategy 2

The main differences are:

  • You don't need to write any other class or implement any interface.
  • Instead you can pass a function reference that will be bound to the method you want.
  • The functions can still be used separately, and the original object can have a default behavior if you want to (the if func == None pattern can be used for that).
  • Indeed, it's clean short and elegant as usual with Python. But you lose information; with no explicit interface, the programmer is assumed as an adult to know what they are doing.

Note that there are 3 ways to dynamically add a method in Python:

  • The way I've shown you. But the method will be static, it won't get the "self" argument passed.

  • Using the class name:

    StrategyExample.execute = func

Here, all the instance will get func as the execute method, and will get self passed as an argument.

  • Binding to an instance only (using the types module):

    strat0.execute = types.MethodType(executeReplacement1, strat0)

    or with Python 2, the class of the instance being changed is also required:

    strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)

This will bind the new method to strat0, and only strat0, like with the first example. But start0.execute() will get self passed as an argument.

If you need to use a reference to the current instance in the function, then you would combine the first and the last method. If you do not:

class StrategyExample:    def __init__(self, func=None):        self.name = "Strategy Example 0"        if func:             self.execute = func    def execute(self):        print(self.name)def executeReplacement1():    print(self.name + " from execute 1")def executeReplacement2():    print(self.name + " from execute 2")if __name__ == "__main__":    strat0 = StrategyExample()    strat1 = StrategyExample(executeReplacement1)    strat1.name = "Strategy Example 1"    strat2 = StrategyExample(executeReplacement2)    strat2.name = "Strategy Example 2"    strat0.execute()    strat1.execute()    strat2.execute()

You will get:

Traceback (most recent call last):  File "test.py", line 28, in <module>    strat1.execute()  File "test.py", line 13, in executeReplacement1    print self.name + " from execute 1"NameError: global name 'self' is not defined

So the proper code would be:

import sysimport typesif sys.version_info[0] > 2:  # Python 3+    create_bound_method = types.MethodTypeelse:    def create_bound_method(func, obj):        return types.MethodType(func, obj, obj.__class__)class StrategyExample:    def __init__(self, func=None):        self.name = "Strategy Example 0"        if func:             self.execute = create_bound_method(func, self)    def execute(self):        print(self.name)def executeReplacement1(self):    print(self.name + " from execute 1")def executeReplacement2(self):    print(self.name + " from execute 2")if __name__ == "__main__":    strat0 = StrategyExample()    strat1 = StrategyExample(executeReplacement1)    strat1.name = "Strategy Example 1"    strat2 = StrategyExample(executeReplacement2)    strat2.name = "Strategy Example 2"    strat0.execute()    strat1.execute()    strat2.execute()

This will output the expected result:

Strategy Example 0Strategy Example 1 from execute 1Strategy Example 2 from execute 2

Of course, in the case the functions cannot be used stand alone anymore, but can still be bound to any other instance of any object, without any interface limitation.


Answering an old question for the Googlers who searched "python strategy pattern" and landed here...

This pattern is practically non-existent in languages that support first class functions. You may want to consider taking advantage of this feature in Python:

def strategy_add(a, b):    return a + bdef strategy_minus(a, b):    return a - bsolver = strategy_addprint solver(1, 2)solver = strategy_minusprint solver(2, 1)

This approach is very clean and simple.

Also, be sure to check out Joe Gregorio's PyCon 2009 talk about Python and design patterns (or lack thereof): http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth


You're right, the wikipedia example isn't helpful. It conflates two things.

  1. Strategy.

  2. Features of Python that simplify the implementation of Strategy. The "there's no need to implement this pattern explicitly" statement is incorrect. You often need to implement Strategy, but Python simplifies this by allowing you to use a function without the overhead of a class wrapper around a function.

First, Strategy.

class AUsefulThing( object ):    def __init__( self, aStrategicAlternative ):        self.howToDoX = aStrategicAlternative    def doX( self, someArg ):        self. howToDoX.theAPImethod( someArg, self )class StrategicAlternative( object ):    passclass AlternativeOne( StrategicAlternative ):    def theAPIMethod( self, someArg, theUsefulThing ):        pass # an implementationclass AlternativeTwo( StrategicAlternative ):    def theAPImethod( self, someArg, theUsefulThing ):        pass # another implementation

Now you can do things like this.

t = AUsefulThing( AlternativeOne() )t.doX( arg )

And it will use the strategy object we created.

Second, Python alternatives.

class AUsefulThing( object ):    def __init__( self, aStrategyFunction ):        self.howToDoX = aStrategyFunction    def doX( self, someArg ):        self.howToDoX( someArg, self )def strategyFunctionOne( someArg, theUsefulThing ):        pass # an implementationdef strategyFunctionTwo( someArg, theUsefulThing ):        pass # another implementation

We can do this.

t= AUsefulThing( strategyFunctionOne )t.doX( anArg )

This will also use a strategy function we provided.