How to implement a strategy pattern with runtime selection of a method?
Question: **_Context_**

I'm trying to implement some variant of strategy pattern in Python 2.7. I want
to be able to instantiate a 'my_strategy' base class, but switch between
different implementations of a 'score' method at run-time. I will have many
common methods in 'my_strategy' but a bunch of 'score' implementations. The
**main** illustrates how I want to use it. Here the scoring implementation is
dummy of course.

**_What I tried (i.e. My code so far)_**

strategy.py:

    
    
    from algo_one import *
    #from algo_two import *
    
    
    class my_strategy ( object ):
    
        def __init__(self, candidate = ""):
            self.candidate = candidate
            self.method = 'default'
            self.no = 10
            self._algo = algo_one
    
        def set_strategy(self, strategy='default'):
            self.strategy = strategy
            if self.strategy == 'algo_one':
                self._algo = algo_one
            elif self.strategy == 'algo_two':
                # self._algo = algo_two
                pass 
            else:
                self._algo = None
    
        def score(self, *args):
            if len(args) > 0:
                self.candidate = args[0]
            self._algo.score(self.candidate)
    
    if  __name__ == "__main__":
        s = my_strategy()  
        s.strategy = 'algo_one'
        s.candidate = "hello world"
        print s.score()  
        print s.score("hi")
        # s.set_method('algo_two')
        # print s.score("hi")
    

I want to save the selected strategy in some sort of private pointer to the
sub-class method.

algo_one.py:

    
    
    from strategy import my_strategy
    class algo_one ( my_strategy ):    
        def score(self, candidate):
            return len(candidate)*self.no 
    

I could have a class-less method, but later I'll need to access public
variables of the base class.

algo_two.py:

    
    
    from strategy import my_strategy
    class algo_two ( my_strategy ):
        def score(self, candidate):
            return len(candidate)*3 
    

I have an empty **init**.py too.

**_The errors_**

**1.** in score self._algo.score(self.candidate)

> TypeError: unbound method score() must be called with algo_one instance as
> first argument (got str instance instead)

**2.** If I uncomment the import of the second strategy:

    
    
    from algo_two import *
    

I get the following error.

> ImportError: cannot import name my_strategy

My guess is that I run into some sort of circular dependency.

**3.**

    
    
    from algo_one import *
    

This is obviously not pretty (unable to detect undefined names), but if I

    
    
    from algo_one import algo_one
    

I get

> ImportError: cannot import name algo_one

**_Question_**

I think the errors are intertwined and that my approach, as a whole, may be
flawed. If not just addressing the error, I'm looking for suggestions to
improve the design. Or any comment, really. Also I'm open to suggestions
regarding the title of this question. Thank you!


Answer: You make it much more complicated than it needs to be. Python functions are
first class objects so the simplest way to implement the strategy pattern in
Python is to pass a 'strategy' function to your "context" object (the one that
uses the strategy). The fine part is that any callable object (ie: any object
implementing the `__call__` method) will work.

    
    
    def default_score_strategy(scorer):
        return len(scorer.candidate) * 3
    
    def universal_answer_score_strategy(scorer):
        return 42 # definitly the universal answer <g>
    
    class ComplicatedStrategy(object):
        def __init__(self, factor):
            self.factor = factor
    
        def __call__(self, scorer):
             return len(scorer.candidate) * self.factor
    
    
    class Scorer(object):
        def __init__(self, candidate="", strategy=default_score_strategy):
            self.candidate = candidate
            self.strategy = strategy
    
        def score(self):
            return self.strategy(self)
    
    s1 = Scorer("foo")
    s2 = Scorer("bar", strategy=universal_answer_score_strategy)
    s3 = Scorer("baaz", strategy=ComplicatedStrategy(365))
    

Note that your strategies dont have to be in the same module as the `Scorer`
class (well, except the default one of course), and that the module containing
the `Scorer` class doesn't have to import the stratgeies modules - nor know
anything about where the strategies are defined:

    
    
    # main.py
    
    from mylib.scores import Scorer
    from myapp.strategies import my_custom_strategy
    
    s = Scorer("yadda", my_custom_strategy)
    

