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

Question:

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?

Asked By: nonopolarity

||

Answers:

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.

Answered By: e-satis

For clarity, I would still use a pseudo-interface:

class CommunicationStrategy(object):
    def execute(self, a, b):
        raise NotImplementedError('execute')

class ConcreteCommunicationStrategyDuck(CommunicationStrategy):
    def execute(self, a, b):
        print "Quack Quack"

class ConcreteCommunicationStrategyCow(CommunicationStrategy):
    def execute(self, a, b):
        print "Mooo"

class ConcreteCommunicationStrategyFrog(CommunicationStrategy):
    def execute(self, a, b):
        print "Ribbit! Ribbit!"
Answered By: Nicolas Dumazet

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 ):
    pass

class AlternativeOne( StrategicAlternative ):
    def theAPIMethod( self, someArg, theUsefulThing ):
        pass # an implementation

class 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 implementation

def 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.

Answered By: S.Lott

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

Answered By: Christian Groleau

I’ve tried to convert the ‘Duck’ example from the 1st chapter (covering Strategy Pattern) of Head First Design Pattern in Python:

class FlyWithRocket():
    def __init__(self):
        pass
    def fly(self):
        print 'FLying with rocket'

class FlyWithWings():
    def __init__(self):
        pass
    def fly(self):
        print 'FLying with wings'

class CantFly():
    def __init__(self):
        pass
    def fly(self):
        print 'I Cant fly'

class SuperDuck:
    def __init__(self):
        pass
    def setFlyingBehaviour(self, fly_obj):
        self.fly_obj = fly_obj
    def perform_fly(self):
        self.fly_obj.fly()

if __name__ == '__main__':
    duck = SuperDuck()
    fly_behaviour = FlyWithRocket()
    #fly_behaviour = FlyWithWings()
    duck.setFlyingBehaviour(fly_behaviour)
    duck.perform_fly()
Answered By: Saurabh Verma
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.