[Tutor] Functional Programming in Python

Steven D'Aprano steve at pearwood.info
Sun Apr 5 07:23:47 CEST 2015


On Sat, Apr 04, 2015 at 09:53:28PM -0400, WolfRage wrote:
> <SNIP>
> So I was surprised I did not get more feedback on my abused coroutine, 
> maybe that is good or bad, not sure.

Perhaps people didn't understand it. Or see it :-)

> Any ways I am on to trying to make that coroutine act more like the 
> State Pattern from Gang of Four. And well reading this:
> http://gameprogrammingpatterns.com/state.html

Now you're fighting the paradigm. Functional programming works best for 
code that avoids side-effects. But changes of state are naturally a 
side-effect! I was sitting, now I am walking. It's still me, but my 
state is different.

Now of course you can emulate state changes using purely functional 
code, but its harder and less efficient.


> I am not sure how to do this:
> class Heroine
> {
> public:
>   virtual void handleInput(Input input)
>   {
>     state_->handleInput(*this, input);
>   }
> 
>   virtual void update()
>   {
>     state_->update(*this);
>   }
> 
>   // Other methods...
> private:
>   HeroineState* state_;
> };
> 
> (Pointing to the different classes. Since C++ has virtual methods but 
> Python does not?) in Python? Do I just reference the new method? Because 
> state_ will always be the correct object?

Hmmm, I'm not sure, but I think that we would say that *all* Python 
methods are virtual in C++ terms. They are all resolved at runtime.

There are a few ways we might do this in Python.

class Heroine:
    def __init__(self):
        self.state = "standing"  # Or use an Enum in Python 3.4

    def jump(self):
        if self.state == "standing":
            self.state = "jumping"
            ...
        elif self.state == "jumping":
            pass

    # Dispatch table of keys to actions.
    KEYMAP = {'b': jump,
              'd': duck,
              'f': run,
              ...
             }

    def handle_keypress(self, key):
        method = self.KEYMAP.get(key, None)
        if method:
            method(self)


We look up the key press, and get a reference to the method itself, not 
the name of the method. (Or None.) We then call that method by providing 
self as the first argument. Nice and easy. It's so easy that I'm not 
sure if it's even worth discussing alternatives.

Another alternative might be to use delegation, or object composition. 
Give the Heroine class all the methods which don't change. For the 
methods which do change, create a separate FooState class for each 
state:

class JumpingState:
    def jump(self):
        pass

    def duck(self):
        ...

class RunningState: 
    ...


class Heroine:
    def __init__(self):
        self.state = StandingState()

    # Dispatch table of keys to method names.
    KEYMAP = {'b': 'jump',
              'd': 'duck',
              'f': 'run',
              ...
             }

    def handle_keypress(self, key):
        name = self.KEYMAP.get(key, None)
        if name:
            newstate = getattr(self.state, name)()
            if newstate:
                self.state = newstate()


Methods of the state object can signal a change of state by returning 
the class representing the new state. Otherwise, they can return None to 
signal no change of state.

Here is how we might do this using inheritence instead of composition. 
Start with a base class and a bunch of default methods which 
conditionally raise:


class BaseHeroine:

    def jump(self):
        if self.__class__ is BaseHeroine:
            raise RuntimeError

    def run(self):
        if self.__class__ is BaseHeroine:
            raise RuntimeError

    def duck(self):
        if self.__class__ is BaseHeroine:
            raise RuntimeError

    # Dispatch table of keys to method names.
    KEYMAP = {'b': 'jump',
              'd': 'duck',
              'f': 'run',
              ...
             }

    def handle_keypress(self, key):
        name = self.KEYMAP.get(key, None)
        if name:
            getattr(self.state, name)()


Now have a bunch of subclasses, one for each state. Override only the 
methods you need. (Remember, the default behaviour for each method is to 
do nothing, if called from a subclass. They only raise if called from 
the base class.)

class JumpHeroine(BaseHeroine):  ...

class StandHeroine(BaseHeroine):  ...

class RunHeroine(BaseHeroine): ...



Now create an instance, and set its state:

heroine = BaseHeroine()
heroine.__class__ = StandHeroine


State transitions are managed by *changing the instance's class*. 
Methods can do that themselves:

    def spam(self):
        self.__class__ = RunHeroine  # Don't instantiate the class.
        print("Spam spam spam loverlllly spam!!!!")




Now, I haven't tested any of the above code, but here is a short and 
simple demonstration showing that it does work:


py> class X:
...     def display(self):
...             print("in X")
...     def method(self):
...             self.display()
...             print(self, type(self), self.__class__)
...
py> class Y(X):
...     def display(self):
...             print("in Y")
...
py> instance = X()
py> instance.method()
in X
<__main__.X object at 0xb7a9d84c> <class '__main__.X'> <class '__main__.X'>
py> instance.__class__ = Y  # dynamically change the class
py> instance.method()
in Y
<__main__.Y object at 0xb7a9d84c> <class '__main__.Y'> <class '__main__.Y'>


Cool, huh? :-)

Having written all this out, I'm now starting to lean towards the 
inheritence version.

Now, how you would this in a purely functional style... I have no idea!


-- 
Steve


More information about the Tutor mailing list