meta-patterns

Design patterns for Python implemented with decorators and classes.


License
MIT
Install
pip install meta-patterns==0.2.1

Documentation

meta-patterns

Design patterns for Python implemented with decorators and classes.

Getting Started

Currently only one design pattern is implemented: Listener.

Listener Pattern

The Listener pattern (otherwise known as the Observer or Publish-Subscribe pattern) is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing (source).

Its use is demonstrated here:

from metapatterns.listener import Listenable, listenable


class Subject(Listenable):
    @listenable
    def myfunc(self, arg1):
        """
        @listenable indicates this function can be 'listened in on'.
        It allows Listeners to hook into it (see MyListener)
        """
        print("myfunc called with arg", arg1)
        return "Hoozah"

    def myfunc2(self, arg1):
        print("myfunc called with arg", arg1)


class MyListener(Subject.Listener):
    """
    Identify this class as a listener of `Subject` through inheritance.
    This makes it so not all listenable methods need to be implemented (they have a default empty implementation in `Subject.Listener`).
    """
    def on_myfunc(self, subject, arg1):
        print("listened in on call to myfunc with arg", arg1)

    def on_myfunc_finished(self, subject, result, arg1):
        print("listened in on result of myfunc with arg", arg1, "and result", result)

    # This cannot be defined because myfunc2 is not a listenable function in Subject
    #def on_myfunc2(self, arg1):
        #pass

We can run this as follows:

if __name__ == "__main__":
    subject = Subject()
    print("# Calling myfunc without listener")
    subject.myfunc(3)

    listener = MyListener()
    subject.add_listener(listener)

    print("\n# Calling myfunc with listener")
    subject.myfunc(5)

    print("\n# Calling myfunc2 with listener")
    subject.myfunc2(7)

    subject.remove_listener(listener)

    print("\n# Calling myfunc again with listener removed")
    subject.myfunc(5)

which gives the output:

# Calling myfunc without listener
myfunc called with arg 3

# Calling myfunc with listener
listened in on call to myfunc with arg 5
myfunc called with arg 5
listened in on result of myfunc with arg 5 and result Hoozah

# Calling myfunc2 with listener
myfunc called with arg 7

# Calling myfunc again with listener removed
myfunc called with arg 5

Subclassing from Subject.Listener has the advantage of raising a TypeError when an on_ function has no matching counterpart in Subject. This can help detect difficult to find problems that arise when changing the name of a function in Subject. Our method includes this check to guard against some issues that come with the loose coupling of the Listener pattern.

Future Work

  • Implement more design patterns.
  • Suggestions? Contact me!