Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Dispatcher pattern
#1
I came across an interesting blog post (in french) about how to implement the dispatcher pattern in Python in a way that is compatible with class inheritance.

After some work I came up with a solution that is much better than the blog's solution because it can be applied without polluting the client classes by making them subclass of some 'Dispatcher' class or the like.

Here is the code, for the connoisseur of smart code, sorry there aren't enough comments and documentation for the time being
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2023 Eric Ringeisen
# SPDX-License-Identifier: MIT
"""An implementation of the dispatcher pattern in Python
that is compatible with class inheritance.
"""

from collections import ChainMap
from functools import partial
from weakref import WeakKeyDictionary

__version__ = '2023.08.07.2'

def _table_decorator(table, key, value):
    table[key] = value
    return value

class Table(ChainMap):
    def setitem(self, key):
        return partial(_table_decorator, self, key)

class PolymorphicDispatcher:
    def __init__(self):
        self._table_dict = WeakKeyDictionary()

    def __get__(self, obj, cls):
        # ignore obj, this is a class feature
        return self.table(cls)

    def table(self, cls):
        try:
            table = self._table_dict[cls]
        except KeyError:
            m = Table()
            for c in cls.mro()[1:]:
                m.maps.append(self.table(c).maps[0])
            table = self._table_dict[cls] = m
        return table

if __name__ == '__main__':
    class Spam:

        on = PolymorphicDispatcher()

        def react(self, key):
            return self.on[key](self)

    @Spam.on.setitem('foo')
    def somefoo(self):
        print(f'somefoo: {self}')

    class Ham(Spam):
        pass

    @Ham.on.setitem('bar')
    def somebar(self):
        print(f'somebar: {self}')

    spam = Spam()
    spam.react('foo')

    ham = Ham()
    ham.react('foo')
    ham.react('bar')
My output:
Output:
somefoo: <__main__.Spam object at 0x7fe4f2479930> somefoo: <__main__.Ham object at 0x7fe4f2479900> somebar: <__main__.Ham object at 0x7fe4f2479900>
Larz60+ and DeaD_EyE like this post
Reply
#2
Uploaded version 2023.08.07.2 because
  1. The dispatcher can point to other values than functions
  2. The tables returned by dispatcher.__get__() now inherit collections.abc.MutableMapping with less code.
  3. Code is amazingly short for what it does, a success for Python standard library's tools.
  4. Interface is better than in previous versions.
Larz60+ likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Switch .. case .. dispatcher based on singledispatch Gribouillis 2 3,823 Feb-02-2020, 05:31 PM
Last Post: Gribouillis

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020