# python-patterns User Guide

**python-patterns** is a curated collection of design patterns and idioms implemented in modern Python. It is not an application you install and run, and not a library you import into your project. It is a **reference catalog**: each pattern lives in its own small, self-contained `.py` file that both *explains* the pattern (in a module docstring) and *demonstrates* it (in runnable, testable examples). You read a file, run it to watch the example execute, then adapt the idiom into your own code.

This guide shows you how to get the collection on your machine, how to run any pattern and see its output, how to read a pattern file, and provides a complete catalog of every pattern in the repository — organized exactly the way the project organizes them.

> [!NOTE]
> The guiding philosophy of this project, stated in its README, is: *"each pattern has its own trade-offs. And you need to pay attention more to **why** you're choosing a certain pattern than to **how** to implement it."* Use this catalog to learn the vocabulary of patterns and their trade-offs — not as code to copy verbatim into production.

---

## 1. Quick Start

You do not need to install anything to read or run a single pattern. If you have Python 3.10 or newer, you can clone the repository and run any pattern file directly.

```bash
git clone https://github.com/faif/python-patterns.git
cd python-patterns
python patterns/creational/factory.py
```

```text
```

The command above prints **nothing** — and that is success. Every pattern file ends with an embedded [doctest](https://docs.python.org/3/library/doctest.html) suite that runs when you execute the file. Silence means every example in the file produced exactly the output it documented.

To actually *see* the pattern run its examples step by step, run the file through doctest in verbose mode:

```bash
python -m doctest -v patterns/creational/factory.py
```

```text
Trying:
    e, g = get_localizer(language="English"), get_localizer(language="Greek")
Expecting nothing
ok
Trying:
    for msg in "dog parrot cat bear".split():
        print(e.localize(msg), g.localize(msg))
Expecting:
    dog σκύλος
    parrot parrot
    cat γάτα
    bear bear
ok
...
2 items passed all tests
```

> [!TIP]
> The fastest way to understand a pattern is to open its file and read the `main()` docstring. That docstring is a transcript of the pattern being used — inputs on `>>>` lines, expected outputs directly beneath. It is simultaneously the documentation, the example, and the test.

---

## 2. Requirements

| Requirement | Detail |
|---|---|
| **Python** | 3.10 or newer (`requires-python = ">=3.10"`; classifiers cover 3.10, 3.11, 3.12, 3.13) |
| **Runtime dependencies** | **None.** Every pattern uses only the Python standard library. |
| **Git** | Recommended, to clone the repository |
| **Operating system** | Any platform with a supported Python (Linux, macOS, Windows) |

> [!IMPORTANT]
> There are **no third-party runtime dependencies**. The `dependencies` list in `pyproject.toml` is empty. Everything you run here works on a clean Python install. The packages listed under `[project.optional-dependencies].dev` (black, flake8, mypy, pytest, tox, and friends) are only needed if you intend to contribute or run the full test/lint suite — see the Admin Guide for those.

---

## 3. Installation

For simply reading and running patterns, **cloning is all you need** — no install step:

=== "Clone (recommended)"

    ```bash
    git clone https://github.com/faif/python-patterns.git
    cd python-patterns
    python patterns/behavioral/observer.py
    ```

=== "Download a single file"

    Each pattern is fully self-contained and uses only the standard library. You can copy a single file out of the repository (for example, from the GitHub web UI) and run it on its own:

    ```bash
    python observer.py
    ```

=== "Editable install (for running tests)"

    If you want to run the project's pytest suite, install the package in editable mode with its dev extras. This registers the `patterns` package so the tests can import it:

    ```bash
    python -m pip install -e ".[dev]"
    ```

> [!NOTE]
> The editable install exists to support **testing and linting**, not to expose an importable API. The package name is `python-patterns` and it packages the `patterns/` directory, but the modules are designed to be *read and run*, not imported as a dependency in your own application.

> [!TIP]
> Want the older Python 2-compatible versions of some patterns? They are preserved on the repository's `legacy` git tag: `git checkout legacy`.

---

## 4. How to Run and Read a Pattern

Every pattern file follows the same convention, so once you learn one, you can navigate all of them.

### 4.1 The anatomy of a pattern file

A typical file has four parts:

1. **A module docstring** at the top explaining *what the pattern is about*, *what the example does*, *where it is used in the real world*, and a one-line `*TL;DR`.
2. **The pattern's classes and functions** — the actual implementation.
3. **A `main()` function whose docstring is a doctest** — the worked example, showing usage and expected output.
4. **A `__main__` guard** that runs the doctests when you execute the file.

```python
def main():
    """
    >>> e, g = get_localizer(language="English"), get_localizer(language="Greek")
    >>> for msg in "dog parrot cat bear".split():
    ...     print(e.localize(msg), g.localize(msg))
    dog σκύλος
    parrot parrot
    cat γάτα
    bear bear
    """


if __name__ == "__main__":
    import doctest

    doctest.testmod()
```

### 4.2 Three ways to run

| Goal | Command | What you see |
|---|---|---|
| Verify a pattern's examples pass | `python patterns/<category>/<name>.py` | Nothing on success; a diff on failure |
| Watch every example execute | `python -m doctest -v patterns/<category>/<name>.py` | Each `>>>` line, its expected output, and `ok` |
| Run **all** in-file examples at once | `pytest --doctest-modules patterns/` | A pytest summary across every pattern |

> [!WARNING]
> A handful of files do more than run doctests in their `__main__` block. For example, `patterns/structural/mvc.py` reads command-line arguments, so running it without arguments raises an `IndexError`. Run it with a registered route and action instead:
>
> ```bash
> python -m patterns.structural.mvc products show_items
> ```
>
> Run the `-m` form from the repository root so Python can resolve the `patterns` package.

### 4.3 The categories

Patterns are grouped the same way the classic *Gang of Four* taxonomy groups them, plus a few project-specific buckets:

```text
patterns/
├── creational/    How objects get made
├── structural/    How objects and classes compose into larger structures
├── behavioral/    How objects communicate and divide responsibility
├── fundamental/   Foundational idioms (delegation)
├── dependency_injection.py   Design-for-testability
└── other/         Useful patterns outside the GoF set
```

---

## 5. Pattern Catalog

This is the heart of the collection. Each entry gives you the one-line summary, the file to open, the command to run it, and the key idea to take away. Patterns are listed in the project's own order.

### 5.1 Creational Patterns

> Patterns that deal with **object creation** — abstracting and controlling how instances are made.

#### Abstract Factory — `patterns/creational/abstract_factory.py`

Provides a way to encapsulate a group of individual factories. In the example, a `PetShop` is handed an animal *factory* (the `Dog` or `Cat` class itself, since classes are callable in Python) and uses it to produce pets without knowing the concrete type up front.

```bash
python -m doctest -v patterns/creational/abstract_factory.py
```

```python
>>> cat_shop = PetShop(Cat)
>>> pet = cat_shop.buy_pet("Lucy")
Here is your lovely Cat<Lucy>
>>> pet.speak()
meow
```

> [!TIP]
> In Python you rarely need a separate factory *class* — a class or a plain function already serves as the "callable that creates an object." This example leans on that fact deliberately.

#### Borg (Monostate) — `patterns/creational/borg.py`

Provides singleton-*like* behavior by **sharing state** across instances instead of sharing identity. Every `Borg` instance points its `__dict__` at one shared class-level dictionary, so attributes set on any instance are visible from all of them — yet the instances remain distinct objects.

```python
>>> rm1, rm2 = YourBorg(), YourBorg()
>>> rm1.state = 'Idle'
>>> rm2.state = 'Running'
>>> print(rm1)   # rm1 reflects rm2's change
Running
>>> rm1 is rm2
False
```

> [!NOTE]
> This is the project's answer to the Singleton question. A true Singleton is listed as an **anti-pattern** (see §7) — the Borg/Monostate idiom shares state without forcing a single instance.

#### Builder — `patterns/creational/builder.py`

Decouples the construction of a complex object from its representation, so the same process can build different products. The example shows two styles: a `Building` base class whose `__init__` calls the build steps, and a `ComplexBuilding` whose steps are driven by an external `construct_building()` function.

```python
>>> house = House()
>>> house
Floor: One | Size: Big
>>> complex_house = construct_building(ComplexHouse)
>>> complex_house
Floor: One | Size: Big and fancy
```

#### Factory — `patterns/creational/factory.py`

Creates objects without the caller specifying the exact class. `get_localizer(language=...)` returns an `EnglishLocalizer` or `GreekLocalizer`; the caller just calls `.localize(...)` the same way regardless.

```python
>>> e, g = get_localizer(language="English"), get_localizer(language="Greek")
>>> g.localize("dog")
'σκύλος'
>>> e.localize("dog")
'dog'
```

#### Lazy Evaluation — `patterns/creational/lazy_evaluation.py`

Delays computing a property's value until it is first accessed, then caches it. Two implementations are shown: a `lazy_property` descriptor and a `lazy_property2` decorator. After first access the computed value lands directly in the instance `__dict__`, so later reads skip the work.

```python
>>> Jhon = Person('Jhon', 'Coder')
>>> Jhon.relatives          # computed on first access
'Many relatives.'
>>> Jhon.parents            # cached after the first call
'Father and mother'
>>> Jhon.call_count2        # the expensive body ran only once
1
```

#### Pool — `patterns/creational/pool.py`

Keeps a set of initialized objects ready to reuse, so you skip the cost of recreating expensive objects. The `ObjectPool` checks an object out of a `queue.Queue` and returns it on context-manager exit (or on garbage collection).

```python
>>> sample_queue.put('yam')
>>> with ObjectPool(sample_queue) as obj:
...    print('Inside with: {}'.format(obj))
Inside with: yam
>>> print('Outside with: {}'.format(sample_queue.get()))   # returned to the pool
Outside with: yam
```

#### Prototype — `patterns/creational/prototype.py`

Creates new objects by **cloning** a prototypical instance instead of subclassing. A `PrototypeDispatcher` registers named prototypes; clients clone from them, overriding only the attributes they need.

```python
>>> prototype = Prototype()
>>> a = prototype.clone(value='a-value', category='a')
>>> b = a.clone(value='b-value', is_checked=True)
>>> print(b.category, b.is_checked)
a True
```

| Pattern | File | TL;DR |
|---|---|---|
| Abstract Factory | `creational/abstract_factory.py` | Encapsulate a group of factories |
| Borg | `creational/borg.py` | Singleton-like shared state across instances |
| Builder | `creational/builder.py` | Separate complex construction from representation |
| Factory | `creational/factory.py` | Create objects without naming the exact class |
| Lazy Evaluation | `creational/lazy_evaluation.py` | Compute a property once, on first use |
| Pool | `creational/pool.py` | Reuse a set of pre-initialized objects |
| Prototype | `creational/prototype.py` | Clone a prototypical instance |

### 5.2 Structural Patterns

> Patterns that define **how classes and objects are composed** to form larger, flexible structures.

#### 3-Tier — `patterns/structural/3-tier.py`

Separates presentation, business logic, and data management into strict layers. The `Ui` talks only to `BusinessLogic`, which talks only to the `Data` store.

```python
>>> ui = Ui()
>>> ui.get_product_information("cheese")
(Fetching from Data Store)
PRODUCT INFORMATION:
Name: Cheese, Price: 2.00, Quantity: 10
```

#### Adapter — `patterns/structural/adapter.py`

Lets an existing class be used through a different interface. `Adapter` wraps an object and exposes its `bark`/`meow`/`speak` methods under a uniform `make_noise()` name, by injecting them into the wrapper's `__dict__`.

```python
>>> objects = [Adapter(Dog(), make_noise=Dog().bark)]
>>> for obj in objects:
...    print("A {0} goes {1}".format(obj.name, obj.make_noise()))
A Dog goes woof!
```

#### Bridge — `patterns/structural/bridge.py`

Decouples an abstraction from its implementation so the two can vary independently. `CircleShape` (abstraction) delegates drawing to an interchangeable `DrawingAPI1`/`DrawingAPI2` (implementation).

```python
>>> shape = CircleShape(1, 2, 3, DrawingAPI1())
>>> shape.scale(2.5)
>>> shape.draw()
API1.circle at 1:2 radius 7.5
```

#### Composite — `patterns/structural/composite.py`

Lets clients treat individual objects and compositions of objects uniformly. A `CompositeGraphic` holds a list of `Graphic` children (which may themselves be composites) and renders them as a tree.

```python
>>> graphic = CompositeGraphic()
>>> graphic.add(graphic1)   # graphic1 is itself a composite of ellipses
>>> graphic.render()
Ellipse: 1
Ellipse: 2
...
```

#### Decorator — `patterns/structural/decorator.py`

Adds behavior to an object without altering its class. `BoldWrapper` and `ItalicWrapper` each wrap a `TextTag` and can be stacked, so the inner text picks up `<b>` then `<i>` tags.

```python
>>> special_hello = ItalicWrapper(BoldWrapper(TextTag("hello, world!")))
>>> special_hello.render()
'<i><b>hello, world!</b></i>'
```

#### Facade — `patterns/structural/facade.py`

Provides one simple interface to a more complex subsystem. `ComputerFacade.start()` hides the orchestration of `CPU`, `Memory`, and `SolidStateDrive` behind a single call.

```python
>>> ComputerFacade().start()
Freezing processor.
Loading from 0x00 data: 'Some data from sector 100 with size 1024'.
Jumping to: 0x00
Executing.
```

#### Flyweight — `patterns/structural/flyweight.py`

Minimizes memory by sharing identical objects instead of duplicating them. `Card.__new__` returns an existing instance from a `WeakValueDictionary` pool when one with the same value+suit already exists, so two "9h" cards are literally the same object.

```python
>>> c1, c2 = Card('9', 'h'), Card('9', 'h')
>>> c1 is c2
True
```

> [!NOTE]
> A second implementation, `patterns/structural/flyweight_with_metaclass.py`, achieves the same pooling through a `FlyweightMeta` metaclass that intercepts instance creation. It is verified by `assert` statements in its `__main__` block rather than a doctest — run it with `python patterns/structural/flyweight_with_metaclass.py` and it passes silently.

#### Front Controller — `patterns/structural/front_controller.py`

Provides a single, centralized entry point that routes incoming requests. `RequestController` validates the request, then a `Dispatcher` sends it to the matching `MobileView` or `TabletView`.

```python
>>> front_controller = RequestController()
>>> front_controller.dispatch_request(Request('mobile'))
Displaying mobile index page
```

#### MVC — `patterns/structural/mvc.py`

Separates data (`Model`), presentation (`View`), and coordination (`Controller`). This file also includes a small `Router` and an executable `__main__` block that dispatches based on command-line arguments.

```python
>>> controller = Controller(ProductModel(), ConsoleView())
>>> controller.show_item_information("cheese")
PRODUCT INFORMATION:
Name: cheese, Price: 2.00, Quantity: 10
```

> [!TIP]
> To run MVC as a tiny CLI app, pass a route and an action:
> ```bash
> python -m patterns.structural.mvc products show_items
> ```

#### Proxy — `patterns/structural/proxy.py`

Adds functionality (logging, access control, caching) in front of a real object without changing its interface. `Proxy.do_the_job` logs the request and only forwards to `RealSubject` for the `admin` user.

```python
>>> proxy = Proxy()
>>> client(proxy, 'anonymous')
[log] Doing the job for anonymous is requested.
[log] I can do the job just for `admins`.
```

| Pattern | File | TL;DR |
|---|---|---|
| 3-Tier | `structural/3-tier.py` | Strict data ↔ logic ↔ presentation separation |
| Adapter | `structural/adapter.py` | Expose one interface as another |
| Bridge | `structural/bridge.py` | Decouple abstraction from implementation |
| Composite | `structural/composite.py` | Treat objects and groups uniformly |
| Decorator | `structural/decorator.py` | Add behavior by wrapping |
| Facade | `structural/facade.py` | One interface over a complex subsystem |
| Flyweight | `structural/flyweight.py` | Share identical objects to save memory |
| Flyweight (metaclass) | `structural/flyweight_with_metaclass.py` | Same pooling via a metaclass |
| Front Controller | `structural/front_controller.py` | Centralized request entry point |
| MVC | `structural/mvc.py` | Model ↔ View ↔ Controller |
| Proxy | `structural/proxy.py` | Funnel operations through a stand-in |

### 5.3 Behavioral Patterns

> Patterns concerned with **communication and responsibility** between objects.

#### Catalog — `patterns/behavioral/catalog.py`

Dispatches to one of several specialized methods based on a parameter passed at construction, using a dictionary instead of `if/elif` chains. Four variants are shown: static-method, instance-method, class-method, and pure-static catalogs.

```python
>>> Catalog('param_value_2').main_method()
'executed method 2!'
```

#### Chain of Responsibility — `patterns/behavioral/chain_of_responsibility.py`

Passes a request along a chain of handlers until one handles it — an object-oriented `if/elif/else` whose branches can be rearranged at runtime. Each `ConcreteHandler` checks a numeric range and either handles the request or forwards it to its successor.

```python
>>> h0.handle(14)
request 14 handled in handler 1
>>> h0.handle(35)
end of chain, no handler for 35
```

#### Chaining Method — `patterns/behavioral/chaining_method.py`

Returns objects from method calls so calls can be chained fluently. `person.do_action(move).amount('5m').stop()` reads as a single sentence.

```python
>>> person.do_action(move).amount('5m').stop()
Jack move 5m then stop
```

#### Command — `patterns/behavioral/command.py`

Bundles an action and its receiver into an object you can invoke later — an object-oriented callback, often with an `undo()`. `MenuItem` holds a `HideFileCommand` or `DeleteFileCommand` and triggers it on press.

```python
>>> item1 = MenuItem(DeleteFileCommand())
>>> item1.on_do_press('test-file')
deleting test-file
>>> item1.on_undo_press()
restoring test-file
```

#### Iterator — `patterns/behavioral/iterator.py` and `iterator_alt.py`

Traverses a container's elements without exposing its internals. Two implementations are provided: `iterator.py` uses a **generator** (`yield from`), and `iterator_alt.py` implements the full **iterator protocol** (`__iter__`/`__next__`) on a `NumberWords` class.

```python
>>> for number in count_to_two():   # generator version
...     print(number)
one
two
```

#### Mediator — `patterns/behavioral/mediator.py`

Encapsulates how a set of objects interact, so they communicate through a mediator rather than directly. `User` instances send messages through a shared `ChatRoom`.

```python
>>> molly = User('Molly')
>>> molly.say("Hi Team! Meeting at 3 PM today.")
'[Molly says]: Hi Team! Meeting at 3 PM today.'
```

#### Memento — `patterns/behavioral/memento.py`

Captures an object's state so it can be restored later. The example builds a `Transaction` guard and a `@Transactional` decorator that roll an object back to its entry state if an operation raises.

```python
>>> num_obj = NumObj(-1)
>>> a_transaction = Transaction(True, num_obj)
>>> # ... mutate, then a failure triggers rollback ...
>>> print(num_obj)
<NumObj: 2>
```

#### Observer — `patterns/behavioral/observer.py`

Maintains a list of dependents and notifies them of state changes. A `Data` subject notifies attached `DecimalViewer` / `HexViewer` observers whenever its value is set.

```python
>>> data1.attach(DecimalViewer())
>>> data1.data = 10
DecimalViewer: Subject Data 1 has data 10
```

> [!TIP]
> The module docstring points to real-world equivalents: Django Signals and Flask Signals are both Observer-pattern implementations you have probably already used.

#### Publish/Subscribe — `patterns/behavioral/publish_subscribe.py`

A `Provider` syndicates messages to zero or more subscribers registered per topic. Publishers push messages; the provider fans them out on `update()`.

```python
>>> jim.subscribe("cartoon")
>>> fftv.publish("cartoon")
>>> message_center.update()
jim got cartoon
```

#### Registry — `patterns/behavioral/registry.py`

Keeps track of every subclass of a base class automatically, using a `RegistryHolder` metaclass that records each new class as it is defined.

```python
>>> class ClassRegistree(BaseRegisteredClass): ...
>>> sorted(RegistryHolder.REGISTRY)
['BaseRegisteredClass', 'ClassRegistree']
```

#### Servant — `patterns/behavioral/servant.py`

Offers shared functionality to a group of unrelated classes without inheritance. `GeometryTools` computes areas, perimeters, and moves for `Circle` and `Rectangle`, neither of which derives from it.

```python
>>> GeometryTools().calculate_area(Circle(5, Position(0, 0)))
78.53981633974483
```

#### Specification — `patterns/behavioral/specification.py`

Lets you recombine business rules by chaining them with boolean logic (`and`, `or`, `not`). Specifications like `UserSpecification` and `SuperUserSpecification` compose into a single rule object.

```python
>>> root = UserSpecification().and_specification(SuperUserSpecification())
>>> root.is_satisfied_by(ivan)
True
```

#### State — `patterns/behavioral/state.py`

Organizes behavior into discrete states and transitions between them. A `Radio` delegates `scan()` and `toggle_amfm()` to its current `AmState` or `FmState`.

```python
>>> radio = Radio()
>>> radio.scan()
Scanning... Station is 1380 AM
```

#### Strategy — `patterns/behavioral/strategy.py`

Selects an algorithm at runtime by passing it in. An `Order` accepts a discount *strategy* function; a descriptor validates that the discount never drives the price negative.

```python
>>> order = Order(100, discount_strategy=ten_percent_discount)
>>> order.apply_discount()
90.0
```

#### Template — `patterns/behavioral/template.py`

Defines the skeleton of an algorithm and lets pluggable components fill in the steps. `template_function` always *gets* then *processes* data, but the getter, converter, and saver are supplied by the caller.

```python
>>> template_function(get_text, to_save=True)
Got `plain-text`
Skip conversion
[SAVE]
`plain-text` was processed
```

#### Visitor — `patterns/behavioral/visitor.py`

Separates an algorithm from the object structure it runs over. A `Visitor` dispatches to a `visit_<ClassName>` method by walking the node's MRO, falling back to `generic_visit`.

```python
>>> visitor = Visitor()
>>> visitor.visit(C())     # C subclasses B, so visit_B is chosen
'visit_B C'
```

> [!TIP]
> The Visitor docstring notes that Python's own `ast.NodeVisitor` (used by tools like `pyflakes`) and the `black` formatter both implement this pattern — a great place to see it at production scale.

| Pattern | File | TL;DR |
|---|---|---|
| Catalog | `behavioral/catalog.py` | Dict-dispatch to specialized methods |
| Chain of Responsibility | `behavioral/chain_of_responsibility.py` | Pass a request down a chain of handlers |
| Chaining Method | `behavioral/chaining_method.py` | Fluent, chainable method calls |
| Command | `behavioral/command.py` | Bundle an action for later, with undo |
| Iterator | `behavioral/iterator.py` | Traverse a container (generator) |
| Iterator (alt.) | `behavioral/iterator_alt.py` | Traverse a container (iterator protocol) |
| Mediator | `behavioral/mediator.py` | Objects interact via a mediator |
| Memento | `behavioral/memento.py` | Capture and restore state |
| Observer | `behavioral/observer.py` | Notify dependents of changes |
| Publish/Subscribe | `behavioral/publish_subscribe.py` | Fan out events to topic subscribers |
| Registry | `behavioral/registry.py` | Auto-track subclasses |
| Servant | `behavioral/servant.py` | Shared services without inheritance |
| Specification | `behavioral/specification.py` | Recombine rules with boolean logic |
| State | `behavioral/state.py` | Behavior driven by discrete states |
| Strategy | `behavioral/strategy.py` | Swap algorithms at runtime |
| Template | `behavioral/template.py` | Fixed skeleton, pluggable steps |
| Visitor | `behavioral/visitor.py` | Separate an operation from a structure |

### 5.4 Fundamental Patterns

> Foundational idioms that other patterns build on.

#### Delegation — `patterns/fundamental/delegation_pattern.py`

An object handles a request by delegating it to a second object. `Delegator.__getattr__` forwards unknown attribute access to its wrapped `Delegate`, achieving the same reuse as inheritance through composition.

```python
>>> delegator = Delegator(Delegate())
>>> delegator.p1
123
>>> delegator.do_something("nothing")
'Doing nothing'
```

### 5.5 Design-for-Testability Patterns

#### Dependency Injection — `patterns/dependency_injection.py`

Supplies an object's dependencies from outside rather than hard-coding them, which makes the dependency easy to substitute or mock in tests. Three variants are demonstrated: **constructor** injection, **parameter** injection, and **setter** injection.

```python
>>> time_with_ci = ConstructorInjection(production_code_time_provider)
>>> time_with_ci.get_current_time_as_html_fragment()
'<span class="tinyBoldText">...</span>'
```

> [!TIP]
> The `midnight_time_provider` stub (`return "24:01"`) in this file shows exactly *why* you inject: it lets the test pin "the current time" to a fixed value instead of depending on the real clock.

### 5.6 Other Patterns (non–Gang of Four)

> Useful patterns outside the classic GoF set.

#### Blackboard — `patterns/other/blackboard.py`

An architectural pattern where several specialized "knowledge sources" (experts) collaborate on a shared `Blackboard` to assemble a solution incrementally. A `Controller` loops, letting each eager expert contribute until progress reaches 100%.

```python
>>> blackboard.add_expert(Student(blackboard))
>>> c = Controller(blackboard)
>>> contributions = c.run_loop()
```

> [!NOTE]
> This example seeds the random generator (`random.seed(1234)`) in its `__main__` block so its doctest output is deterministic.

#### Graph Search — `patterns/other/graph_search.py`

Classic graph traversal algorithms packaged as a `GraphSearch` class: depth-first path finding (`find_path_dfs`, `find_all_paths_dfs`, `find_shortest_path_dfs`) and breadth-first shortest path (`find_shortest_path_bfs`).

```python
>>> graph_search = GraphSearch(graph)
>>> graph_search.find_shortest_path_bfs('A', 'D')
['A', 'B', 'D']
```

#### Hierarchical State Machine (HSM) — `patterns/other/hsm/hsm.py`

A nested/hierarchical finite state machine. A `HierachicalStateMachine` holds `Active`, `Standby`, `Suspect`, and `Failed` states and transitions between them in response to message types such as `"fault trigger"` and `"switchover"`. It defines its own exceptions: `UnsupportedMessageType`, `UnsupportedState`, and `UnsupportedTransition`.

| Pattern | File | TL;DR |
|---|---|---|
| Delegation | `fundamental/delegation_pattern.py` | Reuse via composition, not inheritance |
| Dependency Injection | `dependency_injection.py` | Supply dependencies from outside (3 variants) |
| Blackboard | `other/blackboard.py` | Experts collaborate on shared state |
| Graph Search | `other/graph_search.py` | DFS/BFS path-finding algorithms |
| HSM | `other/hsm/hsm.py` | Hierarchical (nested) finite state machine |

---

## 6. Common Tasks

### Run a single pattern and confirm it works

```bash
python patterns/behavioral/strategy.py
```

```text
```

No output means all of the file's embedded examples passed.

### See a pattern's examples execute, line by line

```bash
python -m doctest -v patterns/behavioral/strategy.py
```

### Run every pattern's in-file examples at once

```bash
pytest --doctest-modules patterns/
```

This discovers and runs the doctest in every `patterns/**/*.py` file.

### Run a pattern that has an executable entry point

`mvc.py` accepts arguments. Provide a route and an action:

```bash
python -m patterns.structural.mvc products show_items
```

```text
PRODUCT LIST:
milk
eggs
cheese
```

### Adapt a pattern into your own code

1. Open the pattern file and read the top docstring for the *why* and the trade-offs.
2. Read the `main()` docstring to see the pattern used end to end.
3. Copy the classes/functions you need into your project. Every pattern is standard-library-only, so nothing else has to come with it.

> [!IMPORTANT]
> These files are teaching examples, intentionally minimal. Before using one in production, adapt it to your needs (error handling, type completeness, edge cases) — and reread the trade-offs in the docstring to confirm the pattern is actually the right tool for your problem.

---

## 7. Anti-Patterns: What This Project Deliberately Does *Not* Implement

The README includes an explicit **Anti-Patterns** section listing common patterns that the project recommends *against* in Python, and which therefore have **no implementation file**:

| Anti-pattern | Why it is discouraged here | Suggested alternative |
|---|---|---|
| **Singleton** | Python modules are already singletons (imported once); an explicit Singleton class adds needless complexity | Module-level variables, or dependency injection |
| **God Object** | Centralizes too much logic in one class; hard to test and maintain | Split into smaller, cohesive classes |
| **Inheritance overuse** | Deep inheritance trees make code brittle | "Favor composition over inheritance" — use composition and delegation |

> [!CAUTION]
> If you came here looking for a **Singleton** or **Interpreter** implementation, note that **neither exists in this repository.** Singleton is intentionally excluded as an anti-pattern (use the `borg` Monostate idiom if you need shared state). The closest creational idioms present are `borg.py` and `factory.py`. Do not assume a file exists for a pattern just because you have seen it elsewhere — consult the catalog in §5, which lists every file that actually ships.

---

## 8. Troubleshooting

### Error: `IndexError: list index out of range` when running `mvc.py`

**Cause:** `patterns/structural/mvc.py` reads `sys.argv[1]` in its `__main__` block. Running it with no arguments leaves nothing to read.

**Solution:** Pass a registered route and action.

```bash
python -m patterns.structural.mvc products show_items
```

**Prevention:** Most pattern files only run doctests in `__main__`; `mvc.py` is the exception that expects CLI arguments. Read the bottom of a file before running it directly.

### Error: `ValueError: Invalid Value for Param: <x>`

**Cause:** The `catalog.py` classes validate their constructor argument against a fixed set of keys (`param_value_1`, `param_value_2`). Any other value raises this.

**Solution:** Pass one of the documented parameter values:

```python
Catalog('param_value_1').main_method()
```

**Prevention:** Check the `_static_method_choices` (or `_instance_method_choices` / `_class_method_choices`) dictionary in the class to see the valid keys.

### Error: `ValueError: Unsupported shape type`

**Cause:** `servant.py`'s `GeometryTools` only knows how to handle `Circle` and `Rectangle`. Passing any other object raises this.

**Solution:** Use a supported shape, or extend `GeometryTools` with a branch for your new shape type.

### Error: `ValueError: Discount cannot be applied due to negative price resulting.`

**Cause:** In `strategy.py`, a discount strategy that would push an `Order`'s price below zero is rejected by the `DiscountStrategyValidator` descriptor; the order's strategy is set to `None` instead.

**Solution:** This is expected behavior, not a bug — choose a discount that leaves a non-negative price, or check `order.discount_strategy` before applying.

### A doctest fails after I edited a pattern file

**Cause:** The `main()` docstring is a doctest. If you change a pattern's behavior or output, the expected text in the docstring no longer matches.

**Solution:** Update the expected output in the `main()` docstring to match the new behavior, then rerun:

```bash
python -m doctest -v patterns/<category>/<name>.py
```

**Prevention:** Treat the docstring examples as tests — keep them in sync with the code.

### Running `python -m patterns.structural.mvc ...` fails with `No module named patterns`

**Cause:** The `-m` form requires Python to find the `patterns` package, which means running from the repository root.

**Solution:** `cd` into the cloned repository root before running the `-m` command.

### `python: command not found` or an old Python runs

**Cause:** Your system Python may be older than 3.10, or `python` may map to Python 2.

**Solution:** Use an explicit interpreter (`python3.12 patterns/...`) and confirm `python --version` reports 3.10 or newer.

---

## 9. Frequently Asked Questions

**Q: Is this a library I can `pip install` and import?**
It is packaged so it can be installed in editable mode for testing, but it is **not designed to be imported as a dependency**. The intent is that you read each pattern and adapt its idiom into your own code. There are no stable public APIs to depend on.

**Q: Do I need to install anything to run a pattern?**
No. Every pattern uses only the Python standard library. Clone the repo (or copy a single file) and run it with Python 3.10+.

**Q: Why does running a pattern print nothing?**
Because it passed. Each file runs its embedded doctests on execution and stays silent on success. Use `python -m doctest -v <file>` to see the examples run.

**Q: How do I see a pattern actually do something?**
Run it in verbose doctest mode (`-m doctest -v`) to watch each example execute, or read the `main()` docstring, which is a transcript of the pattern in use.

**Q: Where is the Singleton pattern?**
There isn't one — Singleton is intentionally listed as an anti-pattern. Use module-level state or dependency injection. If you specifically want shared state across instances, see `borg.py` (the Monostate idiom).

**Q: Where is the Interpreter pattern?**
There is **no `interpreter.py`** in this version of the repository. Use the catalog in §5 as the authoritative list of what actually ships.

**Q: Why are there two iterator files and two flyweight files?**
To show alternative implementations. `iterator.py` uses a generator while `iterator_alt.py` uses the explicit iterator protocol; `flyweight.py` pools via `__new__` while `flyweight_with_metaclass.py` pools via a metaclass. Comparing them is part of the lesson.

**Q: What Python versions are supported?**
Python 3.10 and newer. Python 2-compatible versions of some patterns live on the repository's `legacy` git tag.

**Q: How do I run all the examples to make sure everything works?**
From the repository root: `pytest --doctest-modules patterns/`.

**Q: Can I contribute a new pattern?**
Yes. The repository has a documented contribution process (docstring conventions, linting, tests). See the Admin Guide for the full workflow.

**Q: Are these implementations production-ready?**
They are teaching examples — minimal by design. Adapt them (error handling, typing, edge cases) before production use, and confirm the pattern's trade-offs fit your problem.

---

_Generated June 2026 from faif/python-patterns at 74151cf. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
