How to Implement the State Pattern in Python

Implement the State design pattern in Python by delegating behavior to state objects, letting a media player change actions dynamically without if-else chains.

Medium Python 3.9+ Aug 9, 2026 OOP & classes 12 views 0 copies

Python code

59 lines
Python 3.9+
class State:
    def play(self, player): pass
    def pause(self, player): pass
    def stop(self, player): pass

class PlayingState(State):
    def play(self, player):
        return "Already playing"
    def pause(self, player):
        player.state = PausedState()
        return "Pausing playback"
    def stop(self, player):
        player.state = StoppedState()
        return "Stopping playback"

class PausedState(State):
    def play(self, player):
        player.state = PlayingState()
        return "Resuming playback"
    def pause(self, player):
        return "Already paused"
    def stop(self, player):
        player.state = StoppedState()
        return "Stopping playback"

class StoppedState(State):
    def play(self, player):
        player.state = PlayingState()
        return "Starting playback"
    def pause(self, player):
        return "Cannot pause - no playback"
    def stop(self, player):
        return "Already stopped"

class MediaPlayer:
    def __init__(self):
        self.state = StoppedState()

    def play(self):
        return self.state.play(self)

    def pause(self):
        return self.state.pause(self)

    def stop(self):
        return self.state.stop(self)

    def current_state(self):
        return type(self.state).__name__

if __name__ == "__main__":
    player = MediaPlayer()
    print(f"Initial state: {player.current_state()}")
    print(player.play())
    print(f"State: {player.current_state()}")
    print(player.pause())
    print(f"State: {player.current_state()}")
    print(player.stop())
    print(f"Final state: {player.current_state()}")

Output

stdout
Initial state: StoppedState
Starting playback
State: PlayingState
Pausing playback
State: PausedState
Stopping playback
Final state: StoppedState

How it works

The State pattern encapsulates each possible state in its own class, so the MediaPlayer simply delegates actions to its current state object. Every state class implements the same interface (play, pause, stop) and transitions to the next state by replacing player.state. This eliminates long conditionals and makes adding new states straightforward — just create another class. The current_state method inspects the actual class name for debugging or display.

Common mistakes

  • Having one class with many if-elif branches instead of separate state classes
  • Forgetting to pass `self` to the state method calls inside MediaPlayer
  • Omitting the fallback `pass` methods in the base State class, causing AttributeError for unsupported actions

Variations

  1. Use an enum to store state names and a single switch-like method in the player, though it's less extensible.
  2. Turn state classes into singletons to avoid creating new instances on every transition.

Real-world use cases

  • Modeling workflow statuses in a document processing pipeline where actions depend on the current stage.
  • Building an order management system where each order status defines allowed operations like cancel or ship.
  • Creating a TCP connection handler where network state dictates how to react to incoming packets.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from OOP & classes

Related tutorials and quizzes for this topic.