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

In the 2009 Wikipedia entry for the Strategy Pattern, there's a example written in PHP.

Most other code samples do something like:

a = Context.new(StrategyA.new)
a.execute #=> Doing the task the normal way

b = Context.new(StrategyB.new)
b.execute #=> Doing the task alternatively

c = Context.new(StrategyC.new)
c.execute #=> Doing the task even more alternative

In the Python code a different technique is used with a Submit button. I wonder what the Python code will look like if it also did it the way the other code samples do.

Update: Can it be shorter using first-class functions in Python?


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 execution
Strategy 1
Strategy 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 sys
import types

if sys.version_info[0] > 2:  # Python 3+
    create_bound_method = types.MethodType
else:
    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 0
Strategy Example 1 from execute 1
Strategy 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 + b

def strategy_minus(a, b):
    return a - b

solver = strategy_add
print solver(1, 2)
solver = strategy_minus
print 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