Skip to content

An observer pattern implementation in Python based on jQuery (Python)

License

Notifications You must be signed in to change notification settings

zhenruyan/python-pattern-observer

 
 

Repository files navigation

Observer

image

image

image

image

image

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods1.

The observer.py implementation has a topic-based system, a message filtering type of the publish–subscribe pattern (an observer pattern variation)2. Therefore, a subject can be subdivided into topics and observers can express interest in one (or more) topic and only receive notifications (with any message or not) from that.

The implementation is also based on the jquery publish–subscribe model3 4. So, for convenience, observers will be called handlers and topics will be called events. The observer module has only two members: Event and Observable. An Observable instance allows you connect different handlers to its events. A handler can be any function, method or callable object.

A simpler implementation of the pattern can be found at old.py.

Installation

$ pip install pattern-observer

Alternatively, download the source observer.py and put it in the root directory of your project.

Examples/Features

An event with a handler attached:

from observer import Event, Observable

def clicked(a, b, c=None):
    print('clicked ', a, b, c)

document = Observable()
document.on('click', clicked)  # create event dynamically with a handler attached
document.click(1, 2, c=3)  # notify event handler with arguments

# Verbose version:
document = Observable()
document.click = Event()  # create event
document.events['click'] = document.click  # add it to the dict of events
document.click.on(clicked)  # atach a handler to the event
document.click.trigger(1, 2, c=3)  # notify the handler ...

document.off('click')  # remove an event

# Output:
# clicked  1 2 3

An event with many handlers attached:

def clicked1():
    print('clicked1.')

def clicked2():
    print('clicked2.')

def clicked3():
    print('clicked3.')

document = Observable()
document.on('click', [clicked1, clicked2, clicked3])
document.click.trigger()

# A version using callable objects:
class Clicked:

    def __init__(self, i):
        self.msg = 'clicked{}.'.format(i)

    def __call__(self):  # make it callable
        print(self.msg)

clicked1 = Clicked(1)
clicked2 = Clicked(2)
clicked3 = Clicked(3)
document = Observable()
document.on('click', [clicked1, clicked2, clicked3])
document.trigger('click')

document.off('click', clicked1)  # remove a handler from the event

# Output:
# clicked3.
# clicked2.
# clicked1.

Two events that contains the same handlers attached:

document = Observable()
document.on('clicka', [clicked1, clicked2, clicked3])
document.on('clickb', [clicked1, clicked2, clicked3])

# Simpler:
document.on(['clicka', 'clickb'], [clicked1, clicked2, clicked3])

# More simpler:
document.on('clicka clickb', [clicked1, clicked2, clicked3])

# Trigger two events at once:
document.trigger(['clicka', 'clickb'])  # or
document.trigger('clicka clickb')

# Output:
# clicked1.
# clicked3.
# clicked2.
# clicked1.
# clicked3.
# clicked2.

Add an event with predefined event object that contains many handlers attached:

class ClickEvent(Event):

    def __init__(self):
        self.on(self.clicked1)
        self.on(self.clicked2)
        self.on(self.clicked3)

    def clicked1(self):
        print('clicked1.')

    def clicked2(self):
        print('clicked2.')

    def clicked3(self):
        print('clicked3.')

click_event = ClickEvent()  # predefined event object

document = Observable()
document.on('click', click_event)  # add new entry with predefined obj ...

# Replace event behaviour:
click_event2 = ClickEvent()  # new event object
document.on('click', click_event2)  # update the entry with new obj ...

# Two events can point same event object reference:
document.on('click click_alias', click_event)

# Trigger
document.trigger(['click', 'click_alias'])

# Output:
# clicked1.
# clicked2.
# clicked3.
# clicked1.
# clicked2.
# clicked3.

Add many events with a dictionary:

document = Observable()
document.on({
    'click click_alias': click_event,
    'clicka clickb': [clicked1, clicked2, clicked3],
    'click1': clicked1,
    'click2': clicked2,
    'click3': clicked3})

# Different ways to trigger event handlers:
document.click()
document.clicka.trigger()
document.clickb.trigger()
document.trigger(['click1', 'click2', 'click3'])

# Output:
# ...

Contributing

If you're making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

$ flake8 observer.py tests.py
$ python -m tests.py
$ tox

To get flake8 and tox, just pip install them into your virtualenv.

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
  3. The pull request should work for Python 2.6, 2.7, 3.3, 3.4 and 3.5. Check https://travis-ci.org/fernandojunior/python-pattern-observer/pull_requests and make sure that the tests pass for all supported Python versions.

Synonyms

  • Observer: handler, listener, receiver, consumer, subscriber;
  • Observable: subject, source, provider, generator;
  • Topic: event;
  • Notify: trigger, notify, emit, publish.

Author

Fernando Felix do Nascimento Junior.

License

Released under MIT License.

References


  1. https://en.wikipedia.org/wiki/Observer_pattern

  2. https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern

  3. http://api.jquery.com/trigger/

  4. http://api.jquery.com/on/

About

An observer pattern implementation in Python based on jQuery (Python)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 100.0%