├── .gitignore
├── append_output.sh
├── chaining_method.py
├── decorator.py
├── iterator.py
├── borg.py
├── visitor.py
├── command.py
├── factory_method.py
├── flyweight.py
├── prototype.py
├── bridge.py
├── strategy.py
├── proxy.py
├── lazy_evaluation.py
├── pool.py
├── catalog.py
├── facade.py
├── abstract_factory.py
├── chain.py
├── mvc.py
├── publish_subscribe.py
├── state.py
├── 3-tier.py
├── template.py
├── adapter.py
├── graph_search.py
├── observer.py
├── README.md
├── memento.py
├── mediator.py
└── composite.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 |
--------------------------------------------------------------------------------
/append_output.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | src=$(sed -n -e '/### OUTPUT ###/,$!p' "$1")
6 | output=$(python "$1" | sed 's/^/# /')
7 |
8 | # These are done separately to avoid having to insert a newline, which causes
9 | # problems when the text itself has '\n' in strings
10 | echo "$src" > $1
11 | echo -e "\n### OUTPUT ###" >> $1
12 | echo "$output" >> $1
13 |
--------------------------------------------------------------------------------
/chaining_method.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | class Person(object):
5 |
6 | def __init__(self, name, action):
7 | self.name = name
8 | self.action = action
9 |
10 | def do_action(self):
11 | print(self.name, self.action.name, end=' ')
12 | return self.action
13 |
14 | class Action(object):
15 |
16 | def __init__(self, name):
17 | self.name = name
18 |
19 | def amount(self, val):
20 | print(val, end=' ')
21 | return self
22 |
23 | def stop(self):
24 | print('then stop')
25 |
26 | if __name__ == '__main__':
27 |
28 | move = Action('move')
29 | person = Person('Jack', move)
30 | person.do_action().amount('5m').stop()
31 |
32 | ### OUTPUT ###
33 | # Jack move 5m then stop
34 |
--------------------------------------------------------------------------------
/decorator.py:
--------------------------------------------------------------------------------
1 | """https://docs.python.org/2/library/functools.html#functools.wraps"""
2 | """https://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/739665#739665"""
3 |
4 | from functools import wraps
5 |
6 |
7 | def makebold(fn):
8 | @wraps(fn)
9 | def wrapped():
10 | return "" + fn() + ""
11 | return wrapped
12 |
13 |
14 | def makeitalic(fn):
15 | @wraps(fn)
16 | def wrapped():
17 | return "" + fn() + ""
18 | return wrapped
19 |
20 |
21 | @makebold
22 | @makeitalic
23 | def hello():
24 | """a decorated hello world"""
25 | return "hello world"
26 |
27 | if __name__ == '__main__':
28 | print('result:{} name:{} doc:{}'.format(hello(), hello.__name__, hello.__doc__))
29 |
30 | ### OUTPUT ###
31 | # result:hello world name:hello doc:a decorated hello world
32 |
--------------------------------------------------------------------------------
/iterator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
5 |
6 | Implementation of the iterator pattern with a generator"""
7 |
8 | from __future__ import print_function
9 |
10 |
11 | def count_to(count):
12 | """Counts by word numbers, up to a maximum of five"""
13 | numbers = ["one", "two", "three", "four", "five"]
14 | # enumerate() returns a tuple containing a count (from start which
15 | # defaults to 0) and the values obtained from iterating over sequence
16 | for pos, number in zip(range(count), numbers):
17 | yield number
18 |
19 | # Test the generator
20 | count_to_two = lambda: count_to(2)
21 | count_to_five = lambda: count_to(5)
22 |
23 | print('Counting to two...')
24 | for number in count_to_two():
25 | print(number, end=' ')
26 |
27 | print()
28 |
29 | print('Counting to five...')
30 | for number in count_to_five():
31 | print(number, end=' ')
32 |
33 | print()
34 |
35 | ### OUTPUT ###
36 | # Counting to two...
37 | # one two
38 | # Counting to five...
39 | # one two three four five
40 |
--------------------------------------------------------------------------------
/borg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | class Borg:
6 | __shared_state = {}
7 |
8 | def __init__(self):
9 | self.__dict__ = self.__shared_state
10 | self.state = 'Init'
11 |
12 | def __str__(self):
13 | return self.state
14 |
15 |
16 | class YourBorg(Borg):
17 | pass
18 |
19 | if __name__ == '__main__':
20 | rm1 = Borg()
21 | rm2 = Borg()
22 |
23 | rm1.state = 'Idle'
24 | rm2.state = 'Running'
25 |
26 | print('rm1: {0}'.format(rm1))
27 | print('rm2: {0}'.format(rm2))
28 |
29 | rm2.state = 'Zombie'
30 |
31 | print('rm1: {0}'.format(rm1))
32 | print('rm2: {0}'.format(rm2))
33 |
34 | print('rm1 id: {0}'.format(id(rm1)))
35 | print('rm2 id: {0}'.format(id(rm2)))
36 |
37 | rm3 = YourBorg()
38 |
39 | print('rm1: {0}'.format(rm1))
40 | print('rm2: {0}'.format(rm2))
41 | print('rm3: {0}'.format(rm3))
42 |
43 | ### OUTPUT ###
44 | # rm1: Running
45 | # rm2: Running
46 | # rm1: Zombie
47 | # rm2: Zombie
48 | # rm1 id: 140732837899224
49 | # rm2 id: 140732837899296
50 | # rm1: Init
51 | # rm2: Init
52 | # rm3: Init
53 |
--------------------------------------------------------------------------------
/visitor.py:
--------------------------------------------------------------------------------
1 | """http://peter-hoffmann.com/2010/extrinsic-visitor-pattern-python-inheritance.html"""
2 |
3 |
4 | class Node(object):
5 | pass
6 |
7 |
8 | class A(Node):
9 | pass
10 |
11 |
12 | class B(Node):
13 | pass
14 |
15 |
16 | class C(A, B):
17 | pass
18 |
19 |
20 | class Visitor(object):
21 |
22 | def visit(self, node, *args, **kwargs):
23 | meth = None
24 | for cls in node.__class__.__mro__:
25 | meth_name = 'visit_' + cls.__name__
26 | meth = getattr(self, meth_name, None)
27 | if meth:
28 | break
29 |
30 | if not meth:
31 | meth = self.generic_visit
32 | return meth(node, *args, **kwargs)
33 |
34 | def generic_visit(self, node, *args, **kwargs):
35 | print('generic_visit ' + node.__class__.__name__)
36 |
37 | def visit_B(self, node, *args, **kwargs):
38 | print('visit_B ' + node.__class__.__name__)
39 |
40 |
41 | a = A()
42 | b = B()
43 | c = C()
44 | visitor = Visitor()
45 | visitor.visit(a)
46 | visitor.visit(b)
47 | visitor.visit(c)
48 |
49 | ### OUTPUT ###
50 | # generic_visit A
51 | # visit_B B
52 | # visit_B C
53 |
--------------------------------------------------------------------------------
/command.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 |
6 |
7 | class MoveFileCommand(object):
8 |
9 | def __init__(self, src, dest):
10 | self.src = src
11 | self.dest = dest
12 |
13 | def execute(self):
14 | print('renaming {} to {}'.format(self.src, self.dest))
15 | os.rename(self.src, self.dest)
16 |
17 | def undo(self):
18 | print('renaming {} to {}'.format(self.dest, self.src))
19 | os.rename(self.dest, self.src)
20 |
21 |
22 | def main():
23 | command_stack = []
24 |
25 | # commands are just pushed into the command stack
26 | command_stack.append(MoveFileCommand('foo.txt', 'bar.txt'))
27 | command_stack.append(MoveFileCommand('bar.txt', 'baz.txt'))
28 |
29 | # they can be executed later on
30 | for cmd in command_stack:
31 | cmd.execute()
32 |
33 | # and can also be undone at will
34 | for cmd in reversed(command_stack):
35 | cmd.undo()
36 |
37 | if __name__ == "__main__":
38 | main()
39 |
40 | ### OUTPUT ###
41 | # renaming foo.txt to bar.txt
42 | # renaming bar.txt to baz.txt
43 | # renaming baz.txt to bar.txt
44 | # renaming bar.txt to foo.txt
45 |
--------------------------------------------------------------------------------
/factory_method.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/"""
5 |
6 |
7 | class GreekGetter:
8 |
9 | """A simple localizer a la gettext"""
10 |
11 | def __init__(self):
12 | self.trans = dict(dog="σκύλος", cat="γάτα")
13 |
14 | def get(self, msgid):
15 | """We'll punt if we don't have a translation"""
16 | try:
17 | return self.trans[msgid]
18 | except KeyError:
19 | return str(msgid)
20 |
21 |
22 | class EnglishGetter:
23 |
24 | """Simply echoes the msg ids"""
25 |
26 | def get(self, msgid):
27 | return str(msgid)
28 |
29 |
30 | def get_localizer(language="English"):
31 | """The factory method"""
32 | languages = dict(English=EnglishGetter, Greek=GreekGetter)
33 | return languages[language]()
34 |
35 | # Create our localizers
36 | e, g = get_localizer(language="English"), get_localizer(language="Greek")
37 | # Localize some text
38 | for msgid in "dog parrot cat bear".split():
39 | print(e.get(msgid), g.get(msgid))
40 |
41 | ### OUTPUT ###
42 | # dog σκύλος
43 | # parrot parrot
44 | # cat γάτα
45 | # bear bear
46 |
--------------------------------------------------------------------------------
/flyweight.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://codesnipers.com/?q=python-flyweights"""
5 |
6 | import weakref
7 |
8 |
9 | class Card(object):
10 |
11 | """The object pool. Has builtin reference counting"""
12 | _CardPool = weakref.WeakValueDictionary()
13 |
14 | """Flyweight implementation. If the object exists in the
15 | pool just return it (instead of creating a new one)"""
16 | def __new__(cls, value, suit):
17 | obj = Card._CardPool.get(value + suit, None)
18 | if not obj:
19 | obj = object.__new__(cls)
20 | Card._CardPool[value + suit] = obj
21 | obj.value, obj.suit = value, suit
22 | return obj
23 |
24 | # def __init__(self, value, suit):
25 | # self.value, self.suit = value, suit
26 |
27 | def __repr__(self):
28 | return "" % (self.value, self.suit)
29 |
30 |
31 | if __name__ == '__main__':
32 | # comment __new__ and uncomment __init__ to see the difference
33 | c1 = Card('9', 'h')
34 | c2 = Card('9', 'h')
35 | print(c1, c2)
36 | print(c1 == c2)
37 | print(id(c1), id(c2))
38 |
39 | ### OUTPUT ###
40 | #
41 | # True
42 | # 140368617673296 140368617673296
43 |
--------------------------------------------------------------------------------
/prototype.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import copy
5 |
6 |
7 | class Prototype:
8 |
9 | def __init__(self):
10 | self._objects = {}
11 |
12 | def register_object(self, name, obj):
13 | """Register an object"""
14 | self._objects[name] = obj
15 |
16 | def unregister_object(self, name):
17 | """Unregister an object"""
18 | del self._objects[name]
19 |
20 | def clone(self, name, **attr):
21 | """Clone a registered object and update inner attributes dictionary"""
22 | obj = copy.deepcopy(self._objects.get(name))
23 | obj.__dict__.update(attr)
24 | return obj
25 |
26 |
27 | class A:
28 | def __init__(self):
29 | self.x = 3
30 | self.y = 8
31 | self.z = 15
32 | self.garbage = [38, 11, 19]
33 |
34 | def __str__(self):
35 | return '{} {} {} {}'.format(self.x, self.y, self.z, self.garbage)
36 |
37 |
38 | def main():
39 | a = A()
40 | prototype = Prototype()
41 | prototype.register_object('objecta', a)
42 | b = prototype.clone('objecta')
43 | c = prototype.clone('objecta', x=1, y=2, garbage=[88, 1])
44 | print([str(i) for i in (a, b, c)])
45 |
46 | if __name__ == '__main__':
47 | main()
48 |
49 | ### OUTPUT ###
50 | # ['3 8 15 [38, 11, 19]', '3 8 15 [38, 11, 19]', '1 2 15 [88, 1]']
51 |
--------------------------------------------------------------------------------
/bridge.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Bridge_Pattern#Python"""
5 |
6 |
7 | # ConcreteImplementor 1/2
8 | class DrawingAPI1(object):
9 |
10 | def draw_circle(self, x, y, radius):
11 | print('API1.circle at {}:{} radius {}'.format(x, y, radius))
12 |
13 |
14 | # ConcreteImplementor 2/2
15 | class DrawingAPI2(object):
16 |
17 | def draw_circle(self, x, y, radius):
18 | print('API2.circle at {}:{} radius {}'.format(x, y, radius))
19 |
20 |
21 | # Refined Abstraction
22 | class CircleShape(object):
23 |
24 | def __init__(self, x, y, radius, drawing_api):
25 | self._x = x
26 | self._y = y
27 | self._radius = radius
28 | self._drawing_api = drawing_api
29 |
30 | # low-level i.e. Implementation specific
31 | def draw(self):
32 | self._drawing_api.draw_circle(self._x, self._y, self._radius)
33 |
34 | # high-level i.e. Abstraction specific
35 | def scale(self, pct):
36 | self._radius *= pct
37 |
38 |
39 | def main():
40 | shapes = (
41 | CircleShape(1, 2, 3, DrawingAPI1()),
42 | CircleShape(5, 7, 11, DrawingAPI2())
43 | )
44 |
45 | for shape in shapes:
46 | shape.scale(2.5)
47 | shape.draw()
48 |
49 |
50 | if __name__ == '__main__':
51 | main()
52 |
53 | ### OUTPUT ###
54 | # API1.circle at 1:2 radius 7.5
55 | # API2.circle at 5:7 radius 27.5
56 |
--------------------------------------------------------------------------------
/strategy.py:
--------------------------------------------------------------------------------
1 | # http://stackoverflow.com/questions/963965/how-is-this-strategy-pattern
2 | # -written-in-python-the-sample-in-wikipedia
3 | """
4 | In most of other languages Strategy pattern is implemented via creating some
5 | base strategy interface/abstract class and subclassing it with a number of
6 | concrete strategies (as we can see at
7 | http://en.wikipedia.org/wiki/Strategy_pattern), however Python supports
8 | higher-order functions and allows us to have only one class and inject
9 | functions into it's instances, as shown in this example.
10 | """
11 | import types
12 |
13 |
14 | class StrategyExample:
15 |
16 | def __init__(self, func=None):
17 | self.name = 'Strategy Example 0'
18 | if func is not None:
19 | self.execute = types.MethodType(func, self)
20 |
21 | def execute(self):
22 | print(self.name)
23 |
24 |
25 | def execute_replacement1(self):
26 | print(self.name + ' from execute 1')
27 |
28 |
29 | def execute_replacement2(self):
30 | print(self.name + ' from execute 2')
31 |
32 |
33 | if __name__ == '__main__':
34 | strat0 = StrategyExample()
35 |
36 | strat1 = StrategyExample(execute_replacement1)
37 | strat1.name = 'Strategy Example 1'
38 |
39 | strat2 = StrategyExample(execute_replacement2)
40 | strat2.name = 'Strategy Example 2'
41 |
42 | strat0.execute()
43 | strat1.execute()
44 | strat2.execute()
45 |
46 | ### OUTPUT ###
47 | # Strategy Example 0
48 | # Strategy Example 1 from execute 1
49 | # Strategy Example 2 from execute 2
50 |
--------------------------------------------------------------------------------
/proxy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import time
5 |
6 |
7 | class SalesManager:
8 | def work(self):
9 | print("Sales Manager working...")
10 |
11 | def talk(self):
12 | print("Sales Manager ready to talk")
13 |
14 |
15 | class Proxy:
16 | def __init__(self):
17 | self.busy = 'No'
18 | self.sales = None
19 |
20 | def work(self):
21 | print("Proxy checking for Sales Manager availability")
22 | if self.busy == 'No':
23 | self.sales = SalesManager()
24 | time.sleep(2)
25 | self.sales.talk()
26 | else:
27 | time.sleep(2)
28 | print("Sales Manager is busy")
29 |
30 |
31 | class NoTalkProxy(Proxy):
32 | def __init__(self):
33 | Proxy.__init__(self)
34 |
35 | def work(self):
36 | print("Proxy checking for Sales Manager availability")
37 | time.sleep(2)
38 | print("This Sales Manager will not talk to you whether he/she is busy or not")
39 |
40 |
41 | if __name__ == '__main__':
42 | p = Proxy()
43 | p.work()
44 | p.busy = 'Yes'
45 | p.work()
46 | p = NoTalkProxy()
47 | p.work()
48 | p.busy = 'Yes'
49 | p.work()
50 |
51 | ### OUTPUT ###
52 | # Proxy checking for Sales Manager availability
53 | # Sales Manager ready to talk
54 | # Proxy checking for Sales Manager availability
55 | # Sales Manager is busy
56 | # Proxy checking for Sales Manager availability
57 | # This Sales Manager will not talk to you whether he/she is busy or not
58 | # Proxy checking for Sales Manager availability
59 | # This Sales Manager will not talk to you whether he/she is busy or not
60 |
--------------------------------------------------------------------------------
/lazy_evaluation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Lazily-evaluated property pattern in Python.
6 |
7 | https://en.wikipedia.org/wiki/Lazy_evaluation
8 | http://stevenloria.com/lazy-evaluated-properties-in-python/
9 | """
10 |
11 |
12 | def lazy_property(fn):
13 | """Decorator that makes a property lazy-evaluated."""
14 | attr_name = '_lazy_' + fn.__name__
15 |
16 | @property
17 | def _lazy_property(self):
18 | if not hasattr(self, attr_name):
19 | setattr(self, attr_name, fn(self))
20 | return getattr(self, attr_name)
21 | return _lazy_property
22 |
23 |
24 | class Person(object):
25 | def __init__(self, name, occupation):
26 | self.name = name
27 | self.occupation = occupation
28 |
29 | @lazy_property
30 | def relatives(self):
31 | # Get all relatives, let's assume that it costs much time.
32 | relatives = "Many relatives."
33 | return relatives
34 |
35 |
36 | def main():
37 | Jhon = Person('Jhon', 'Coder')
38 | print("Name: {0} Occupation: {1}".format(Jhon.name, Jhon.occupation))
39 | print("Before we access `relatives`:")
40 | print(Jhon.__dict__)
41 | print("Jhon's relatives: {0}".format(Jhon.relatives))
42 | print("After we've accessed `relatives`:")
43 | print(Jhon.__dict__)
44 |
45 |
46 | if __name__ == '__main__':
47 | main()
48 |
49 | ### OUTPUT ###
50 | # Name: Jhon Occupation: Coder
51 | # Before we access `relatives`:
52 | # {'name': 'Jhon', 'occupation': 'Coder'}
53 | # Jhon's relatives: Many relatives.
54 | # After we've accessed `relatives`:
55 | # {'_lazy_relatives': 'Many relatives.', 'name': 'Jhon', 'occupation': 'Coder'}
56 |
--------------------------------------------------------------------------------
/pool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://stackoverflow.com/questions/1514120/python-implementation-of-the-object-pool-design-pattern"""
5 |
6 |
7 | class QueueObject():
8 |
9 | def __init__(self, queue, auto_get=False):
10 | self._queue = queue
11 | self.object = self._queue.get() if auto_get else None
12 |
13 | def __enter__(self):
14 | if self.object is None:
15 | self.object = self._queue.get()
16 | return self.object
17 |
18 | def __exit__(self, Type, value, traceback):
19 | if self.object is not None:
20 | self._queue.put(self.object)
21 | self.object = None
22 |
23 | def __del__(self):
24 | if self.object is not None:
25 | self._queue.put(self.object)
26 | self.object = None
27 |
28 |
29 | def main():
30 | try:
31 | import queue
32 | except ImportError: # python 2.x compatibility
33 | import Queue as queue
34 |
35 | def test_object(queue):
36 | queue_object = QueueObject(queue, True)
37 | print('Inside func: {}'.format(queue_object.object))
38 |
39 | sample_queue = queue.Queue()
40 |
41 | sample_queue.put('yam')
42 | with QueueObject(sample_queue) as obj:
43 | print('Inside with: {}'.format(obj))
44 | print('Outside with: {}'.format(sample_queue.get()))
45 |
46 | sample_queue.put('sam')
47 | test_object(sample_queue)
48 | print('Outside func: {}'.format(sample_queue.get()))
49 |
50 | if not sample_queue.empty():
51 | print(sample_queue.get())
52 |
53 |
54 | if __name__ == '__main__':
55 | main()
56 |
57 | ### OUTPUT ###
58 | # Inside with: yam
59 | # Outside with: yam
60 | # Inside func: sam
61 | # Outside func: sam
62 |
--------------------------------------------------------------------------------
/catalog.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | A class that uses different static function depending of a parameter passed in
6 | init. Note the use of a single dictionary instead of multiple conditions
7 | """
8 | __author__ = "Ibrahim Diop "
9 | __gist__ = ""
10 |
11 |
12 | class Catalog():
13 |
14 | """
15 | catalog of multiple static methods that are executed depending on an init
16 | parameter
17 | """
18 |
19 | def __init__(self, param):
20 |
21 | # dictionary that will be used to determine which static method is
22 | # to be executed but that will be also used to store possible param
23 | # value
24 | self._static_method_choices = {'param_value_1': self._static_method_1,
25 | 'param_value_2': self._static_method_2}
26 |
27 | # simple test to validate param value
28 | if param in self._static_method_choices.keys():
29 | self.param = param
30 | else:
31 | raise ValueError("Invalid Value for Param: {0}".format(param))
32 |
33 | @staticmethod
34 | def _static_method_1():
35 | print("executed method 1!")
36 |
37 | @staticmethod
38 | def _static_method_2():
39 | print("executed method 2!")
40 |
41 | def main_method(self):
42 | """
43 | will execute either _static_method_1 or _static_method_2
44 | depending on self.param value
45 | """
46 | self._static_method_choices[self.param]()
47 |
48 |
49 | def main():
50 | """
51 | >>> c = Catalog('param_value_1').main_method()
52 | executed method 1!
53 | >>> Catalog('param_value_2').main_method()
54 | executed method 2!
55 | """
56 |
57 | test = Catalog('param_value_2')
58 | test.main_method()
59 |
60 | if __name__ == "__main__":
61 | main()
62 |
63 | ### OUTPUT ###
64 | # executed method 2!
65 |
--------------------------------------------------------------------------------
/facade.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import time
5 |
6 | SLEEP = 0.5
7 |
8 |
9 | # Complex Parts
10 | class TC1:
11 |
12 | def run(self):
13 | print("###### In Test 1 ######")
14 | time.sleep(SLEEP)
15 | print("Setting up")
16 | time.sleep(SLEEP)
17 | print("Running test")
18 | time.sleep(SLEEP)
19 | print("Tearing down")
20 | time.sleep(SLEEP)
21 | print("Test Finished\n")
22 |
23 |
24 | class TC2:
25 |
26 | def run(self):
27 | print("###### In Test 2 ######")
28 | time.sleep(SLEEP)
29 | print("Setting up")
30 | time.sleep(SLEEP)
31 | print("Running test")
32 | time.sleep(SLEEP)
33 | print("Tearing down")
34 | time.sleep(SLEEP)
35 | print("Test Finished\n")
36 |
37 |
38 | class TC3:
39 |
40 | def run(self):
41 | print("###### In Test 3 ######")
42 | time.sleep(SLEEP)
43 | print("Setting up")
44 | time.sleep(SLEEP)
45 | print("Running test")
46 | time.sleep(SLEEP)
47 | print("Tearing down")
48 | time.sleep(SLEEP)
49 | print("Test Finished\n")
50 |
51 |
52 | # Facade
53 | class TestRunner:
54 |
55 | def __init__(self):
56 | self.tc1 = TC1()
57 | self.tc2 = TC2()
58 | self.tc3 = TC3()
59 | self.tests = [i for i in (self.tc1, self.tc2, self.tc3)]
60 |
61 | def runAll(self):
62 | [i.run() for i in self.tests]
63 |
64 |
65 | # Client
66 | if __name__ == '__main__':
67 | testrunner = TestRunner()
68 | testrunner.runAll()
69 |
70 | ### OUTPUT ###
71 | # ###### In Test 1 ######
72 | # Setting up
73 | # Running test
74 | # Tearing down
75 | # Test Finished
76 | #
77 | # ###### In Test 2 ######
78 | # Setting up
79 | # Running test
80 | # Tearing down
81 | # Test Finished
82 | #
83 | # ###### In Test 3 ######
84 | # Setting up
85 | # Running test
86 | # Tearing down
87 | # Test Finished
88 | #
89 |
--------------------------------------------------------------------------------
/abstract_factory.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | # http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
5 |
6 | """Implementation of the abstract factory pattern"""
7 |
8 | import random
9 |
10 |
11 | class PetShop:
12 |
13 | """A pet shop"""
14 |
15 | def __init__(self, animal_factory=None):
16 | """pet_factory is our abstract factory. We can set it at will."""
17 |
18 | self.pet_factory = animal_factory
19 |
20 | def show_pet(self):
21 | """Creates and shows a pet using the abstract factory"""
22 |
23 | pet = self.pet_factory.get_pet()
24 | print("We have a lovely {}".format(pet))
25 | print("It says {}".format(pet.speak()))
26 | print("We also have {}".format(self.pet_factory.get_food()))
27 |
28 |
29 | # Stuff that our factory makes
30 |
31 | class Dog:
32 |
33 | def speak(self):
34 | return "woof"
35 |
36 | def __str__(self):
37 | return "Dog"
38 |
39 |
40 | class Cat:
41 |
42 | def speak(self):
43 | return "meow"
44 |
45 | def __str__(self):
46 | return "Cat"
47 |
48 |
49 | # Factory classes
50 |
51 | class DogFactory:
52 |
53 | def get_pet(self):
54 | return Dog()
55 |
56 | def get_food(self):
57 | return "dog food"
58 |
59 |
60 | class CatFactory:
61 |
62 | def get_pet(self):
63 | return Cat()
64 |
65 | def get_food(self):
66 | return "cat food"
67 |
68 |
69 | # Create the proper family
70 | def get_factory():
71 | """Let's be dynamic!"""
72 | return random.choice([DogFactory, CatFactory])()
73 |
74 |
75 | # Show pets with various factories
76 | if __name__ == "__main__":
77 | for i in range(3):
78 | shop = PetShop(get_factory())
79 | shop.show_pet()
80 | print("=" * 20)
81 |
82 | ### OUTPUT ###
83 | # We have a lovely Dog
84 | # It says woof
85 | # We also have dog food
86 | # ====================
87 | # We have a lovely Dog
88 | # It says woof
89 | # We also have dog food
90 | # ====================
91 | # We have a lovely Cat
92 | # It says meow
93 | # We also have cat food
94 | # ====================
95 |
--------------------------------------------------------------------------------
/chain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://www.testingperspective.com/wiki/doku.php/collaboration/chetan/designpatternsinpython/chain-of-responsibilitypattern"""
5 |
6 | class Handler:
7 | def __init__(self,successor):
8 | self._successor = successor;
9 | def handle(self,request):
10 | i = self._handle(request)
11 | if not i:
12 | self._successor.handle(request)
13 | def _handle(self, request):
14 | raise NotImplementedError('Must provide implementation in subclass.')
15 |
16 |
17 | class ConcreteHandler1(Handler):
18 |
19 | def _handle(self, request):
20 | if 0 < request <= 10:
21 | print('request {} handled in handler 1'.format(request))
22 | return True
23 |
24 | class ConcreteHandler2(Handler):
25 |
26 | def _handle(self, request):
27 | if 10 < request <= 20:
28 | print('request {} handled in handler 2'.format(request))
29 | return True
30 |
31 | class ConcreteHandler3(Handler):
32 |
33 | def _handle(self, request):
34 | if 20 < request <= 30:
35 | print('request {} handled in handler 3'.format(request))
36 | return True
37 | class DefaultHandler(Handler):
38 |
39 | def _handle(self, request):
40 | print('end of chain, no handler for {}'.format(request))
41 | return True
42 |
43 |
44 | class Client:
45 | def __init__(self):
46 | self.handler = ConcreteHandler1(ConcreteHandler3(ConcreteHandler2(DefaultHandler(None))))
47 | def delegate(self, requests):
48 | for request in requests:
49 | self.handler.handle(request)
50 |
51 |
52 | if __name__ == "__main__":
53 | client = Client()
54 | requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
55 | client.delegate(requests)
56 |
57 | ### OUTPUT ###
58 | # request 2 handled in handler 1
59 | # request 5 handled in handler 1
60 | # request 14 handled in handler 2
61 | # request 22 handled in handler 3
62 | # request 18 handled in handler 2
63 | # request 3 handled in handler 1
64 | # end of chain, no handler for 35
65 | # request 27 handled in handler 3
66 | # request 20 handled in handler 2
67 |
--------------------------------------------------------------------------------
/mvc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | class Model(object):
6 |
7 | products = {
8 | 'milk': {'price': 1.50, 'quantity': 10},
9 | 'eggs': {'price': 0.20, 'quantity': 100},
10 | 'cheese': {'price': 2.00, 'quantity': 10}
11 | }
12 |
13 |
14 | class View(object):
15 |
16 | def product_list(self, product_list):
17 | print('PRODUCT LIST:')
18 | for product in product_list:
19 | print(product)
20 | print('')
21 |
22 | def product_information(self, product, product_info):
23 | print('PRODUCT INFORMATION:')
24 | print('Name: %s, Price: %.2f, Quantity: %d\n' %
25 | (product.title(), product_info.get('price', 0),
26 | product_info.get('quantity', 0)))
27 |
28 | def product_not_found(self, product):
29 | print('That product "%s" does not exist in the records' % product)
30 |
31 |
32 | class Controller(object):
33 |
34 | def __init__(self):
35 | self.model = Model()
36 | self.view = View()
37 |
38 | def get_product_list(self):
39 | product_list = self.model.products.keys()
40 | self.view.product_list(product_list)
41 |
42 | def get_product_information(self, product):
43 | product_info = self.model.products.get(product, None)
44 | if product_info is not None:
45 | self.view.product_information(product, product_info)
46 | else:
47 | self.view.product_not_found(product)
48 |
49 |
50 | if __name__ == '__main__':
51 |
52 | controller = Controller()
53 | controller.get_product_list()
54 | controller.get_product_information('cheese')
55 | controller.get_product_information('eggs')
56 | controller.get_product_information('milk')
57 | controller.get_product_information('arepas')
58 |
59 | ### OUTPUT ###
60 | # PRODUCT LIST:
61 | # cheese
62 | # eggs
63 | # milk
64 | #
65 | # PRODUCT INFORMATION:
66 | # Name: Cheese, Price: 2.00, Quantity: 10
67 | #
68 | # PRODUCT INFORMATION:
69 | # Name: Eggs, Price: 0.20, Quantity: 100
70 | #
71 | # PRODUCT INFORMATION:
72 | # Name: Milk, Price: 1.50, Quantity: 10
73 | #
74 | # That product "arepas" does not exist in the records
75 |
--------------------------------------------------------------------------------
/publish_subscribe.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Reference: http://www.slideshare.net/ishraqabd/publish-subscribe-model-overview-13368808
6 | Author: https://github.com/HanWenfang
7 | """
8 |
9 |
10 | class Provider:
11 |
12 | def __init__(self):
13 | self.msg_queue = []
14 | self.subscribers = {}
15 |
16 | def notify(self, msg):
17 | self.msg_queue.append(msg)
18 |
19 | def subscribe(self, msg, subscriber):
20 | self.subscribers.setdefault(msg, []).append(subscriber)
21 |
22 | def unsubscribe(self, msg, subscriber):
23 | self.subscribers[msg].remove(subscriber)
24 |
25 | def update(self):
26 | for msg in self.msg_queue:
27 | if msg in self.subscribers:
28 | for sub in self.subscribers[msg]:
29 | sub.run(msg)
30 | self.msg_queue = []
31 |
32 |
33 | class Publisher:
34 |
35 | def __init__(self, msg_center):
36 | self.provider = msg_center
37 |
38 | def publish(self, msg):
39 | self.provider.notify(msg)
40 |
41 |
42 | class Subscriber:
43 |
44 | def __init__(self, name, msg_center):
45 | self.name = name
46 | self.provider = msg_center
47 |
48 | def subscribe(self, msg):
49 | self.provider.subscribe(msg, self)
50 |
51 | def run(self, msg):
52 | print("{} got {}".format(self.name, msg))
53 |
54 |
55 | def main():
56 | message_center = Provider()
57 |
58 | fftv = Publisher(message_center)
59 |
60 | jim = Subscriber("jim", message_center)
61 | jim.subscribe("cartoon")
62 | jack = Subscriber("jack", message_center)
63 | jack.subscribe("music")
64 | gee = Subscriber("gee", message_center)
65 | gee.subscribe("movie")
66 |
67 | fftv.publish("cartoon")
68 | fftv.publish("music")
69 | fftv.publish("ads")
70 | fftv.publish("movie")
71 | fftv.publish("cartoon")
72 | fftv.publish("cartoon")
73 | fftv.publish("movie")
74 | fftv.publish("blank")
75 |
76 | message_center.update()
77 |
78 |
79 | if __name__ == "__main__":
80 | main()
81 |
82 | ### OUTPUT ###
83 | # jim got cartoon
84 | # jack got music
85 | # gee got movie
86 | # jim got cartoon
87 | # jim got cartoon
88 | # gee got movie
89 |
--------------------------------------------------------------------------------
/state.py:
--------------------------------------------------------------------------------
1 | """Implementation of the state pattern"""
2 |
3 | # http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
4 | from __future__ import print_function
5 |
6 |
7 | class State(object):
8 |
9 | """Base state. This is to share functionality"""
10 |
11 | def scan(self):
12 | """Scan the dial to the next station"""
13 | self.pos += 1
14 | if self.pos == len(self.stations):
15 | self.pos = 0
16 | print("Scanning... Station is", self.stations[self.pos], self.name)
17 |
18 |
19 | class AmState(State):
20 |
21 | def __init__(self, radio):
22 | self.radio = radio
23 | self.stations = ["1250", "1380", "1510"]
24 | self.pos = 0
25 | self.name = "AM"
26 |
27 | def toggle_amfm(self):
28 | print("Switching to FM")
29 | self.radio.state = self.radio.fmstate
30 |
31 |
32 | class FmState(State):
33 |
34 | def __init__(self, radio):
35 | self.radio = radio
36 | self.stations = ["81.3", "89.1", "103.9"]
37 | self.pos = 0
38 | self.name = "FM"
39 |
40 | def toggle_amfm(self):
41 | print("Switching to AM")
42 | self.radio.state = self.radio.amstate
43 |
44 |
45 | class Radio(object):
46 |
47 | """A radio. It has a scan button, and an AM/FM toggle switch."""
48 |
49 | def __init__(self):
50 | """We have an AM state and an FM state"""
51 | self.amstate = AmState(self)
52 | self.fmstate = FmState(self)
53 | self.state = self.amstate
54 |
55 | def toggle_amfm(self):
56 | self.state.toggle_amfm()
57 |
58 | def scan(self):
59 | self.state.scan()
60 |
61 |
62 | # Test our radio out
63 | if __name__ == '__main__':
64 | radio = Radio()
65 | actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
66 | actions *= 2
67 |
68 | for action in actions:
69 | action()
70 |
71 | ### OUTPUT ###
72 | # Scanning... Station is 1380 AM
73 | # Scanning... Station is 1510 AM
74 | # Switching to FM
75 | # Scanning... Station is 89.1 FM
76 | # Scanning... Station is 103.9 FM
77 | # Scanning... Station is 81.3 FM
78 | # Scanning... Station is 89.1 FM
79 | # Switching to AM
80 | # Scanning... Station is 1250 AM
81 | # Scanning... Station is 1380 AM
82 |
--------------------------------------------------------------------------------
/3-tier.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | class Data(object):
6 | """ Data Store Class """
7 |
8 | products = {
9 | 'milk': {'price': 1.50, 'quantity': 10},
10 | 'eggs': {'price': 0.20, 'quantity': 100},
11 | 'cheese': {'price': 2.00, 'quantity': 10}
12 | }
13 |
14 | def __get__(self, obj, klas):
15 | print ("(Fetching from Data Store)")
16 | return {'products': self.products}
17 |
18 |
19 | class BusinessLogic(object):
20 |
21 | """ Business logic holding data store instances """
22 |
23 | data = Data()
24 |
25 | def product_list(self):
26 | return self.data['products'].keys()
27 |
28 | def product_information(self, product):
29 | return self.data['products'].get(product, None)
30 |
31 |
32 | class Ui(object):
33 | """ UI interaction class """
34 |
35 | def __init__(self):
36 | self.business_logic = BusinessLogic()
37 |
38 | def get_product_list(self):
39 | print('PRODUCT LIST:')
40 | for product in self.business_logic.product_list():
41 | print(product)
42 | print('')
43 |
44 | def get_product_information(self, product):
45 | product_info = self.business_logic.product_information(product)
46 | if product_info:
47 | print('PRODUCT INFORMATION:')
48 | print('Name: {0}, Price: {1:.2f}, Quantity: {2:}'.format(
49 | product.title(), product_info.get('price', 0),
50 | product_info.get('quantity', 0)))
51 | else:
52 | print('That product "{0}" does not exist in the records'.format(
53 | product))
54 |
55 |
56 | def main():
57 | ui = Ui()
58 | ui.get_product_list()
59 | ui.get_product_information('cheese')
60 | ui.get_product_information('eggs')
61 | ui.get_product_information('milk')
62 | ui.get_product_information('arepas')
63 |
64 | if __name__ == '__main__':
65 | main()
66 |
67 | ### OUTPUT ###
68 | # PRODUCT LIST:
69 | # (Fetching from Data Store)
70 | # cheese
71 | # eggs
72 | # milk
73 | #
74 | # (Fetching from Data Store)
75 | # PRODUCT INFORMATION:
76 | # Name: Cheese, Price: 2.00, Quantity: 10
77 | # (Fetching from Data Store)
78 | # PRODUCT INFORMATION:
79 | # Name: Eggs, Price: 0.20, Quantity: 100
80 | # (Fetching from Data Store)
81 | # PRODUCT INFORMATION:
82 | # Name: Milk, Price: 1.50, Quantity: 10
83 | # (Fetching from Data Store)
84 | # That product "arepas" does not exist in the records
85 |
--------------------------------------------------------------------------------
/template.py:
--------------------------------------------------------------------------------
1 | """http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
2 |
3 | An example of the Template pattern in Python"""
4 |
5 | ingredients = "spam eggs apple"
6 | line = '-' * 10
7 |
8 |
9 | # Skeletons
10 | def iter_elements(getter, action):
11 | """Template skeleton that iterates items"""
12 | for element in getter():
13 | action(element)
14 | print(line)
15 |
16 |
17 | def rev_elements(getter, action):
18 | """Template skeleton that iterates items in reverse order"""
19 | for element in getter()[::-1]:
20 | action(element)
21 | print(line)
22 |
23 |
24 | # Getters
25 | def get_list():
26 | return ingredients.split()
27 |
28 |
29 | def get_lists():
30 | return [list(x) for x in ingredients.split()]
31 |
32 |
33 | # Actions
34 | def print_item(item):
35 | print(item)
36 |
37 |
38 | def reverse_item(item):
39 | print(item[::-1])
40 |
41 |
42 | # Makes templates
43 | def make_template(skeleton, getter, action):
44 | """Instantiate a template method with getter and action"""
45 | def template():
46 | skeleton(getter, action)
47 | return template
48 |
49 | # Create our template functions
50 | templates = [make_template(s, g, a)
51 | for g in (get_list, get_lists)
52 | for a in (print_item, reverse_item)
53 | for s in (iter_elements, rev_elements)]
54 |
55 | # Execute them
56 | for template in templates:
57 | template()
58 |
59 | ### OUTPUT ###
60 | # spam
61 | # ----------
62 | # eggs
63 | # ----------
64 | # apple
65 | # ----------
66 | # apple
67 | # ----------
68 | # eggs
69 | # ----------
70 | # spam
71 | # ----------
72 | # maps
73 | # ----------
74 | # sgge
75 | # ----------
76 | # elppa
77 | # ----------
78 | # elppa
79 | # ----------
80 | # sgge
81 | # ----------
82 | # maps
83 | # ----------
84 | # ['s', 'p', 'a', 'm']
85 | # ----------
86 | # ['e', 'g', 'g', 's']
87 | # ----------
88 | # ['a', 'p', 'p', 'l', 'e']
89 | # ----------
90 | # ['a', 'p', 'p', 'l', 'e']
91 | # ----------
92 | # ['e', 'g', 'g', 's']
93 | # ----------
94 | # ['s', 'p', 'a', 'm']
95 | # ----------
96 | # ['m', 'a', 'p', 's']
97 | # ----------
98 | # ['s', 'g', 'g', 'e']
99 | # ----------
100 | # ['e', 'l', 'p', 'p', 'a']
101 | # ----------
102 | # ['e', 'l', 'p', 'p', 'a']
103 | # ----------
104 | # ['s', 'g', 'g', 'e']
105 | # ----------
106 | # ['m', 'a', 'p', 's']
107 | # ----------
108 |
--------------------------------------------------------------------------------
/adapter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/"""
5 |
6 |
7 | class Dog(object):
8 | def __init__(self):
9 | self.name = "Dog"
10 |
11 | def bark(self):
12 | return "woof!"
13 |
14 |
15 | class Cat(object):
16 | def __init__(self):
17 | self.name = "Cat"
18 |
19 | def meow(self):
20 | return "meow!"
21 |
22 |
23 | class Human(object):
24 | def __init__(self):
25 | self.name = "Human"
26 |
27 | def speak(self):
28 | return "'hello'"
29 |
30 |
31 | class Car(object):
32 | def __init__(self):
33 | self.name = "Car"
34 |
35 | def make_noise(self, octane_level):
36 | return "vroom{0}".format("!" * octane_level)
37 |
38 |
39 | class Adapter(object):
40 |
41 | """
42 | Adapts an object by replacing methods.
43 | Usage:
44 | dog = Dog
45 | dog = Adapter(dog, dict(make_noise=dog.bark))
46 |
47 | >>> objects = []
48 | >>> dog = Dog()
49 | >>> objects.append(Adapter(dog, make_noise=dog.bark))
50 | >>> cat = Cat()
51 | >>> objects.append(Adapter(cat, make_noise=cat.meow))
52 | >>> human = Human()
53 | >>> objects.append(Adapter(human, make_noise=human.speak))
54 | >>> car = Car()
55 | >>> car_noise = lambda: car.make_noise(3)
56 | >>> objects.append(Adapter(car, make_noise=car_noise))
57 |
58 | >>> for obj in objects:
59 | ... print('A {} goes {}'.format(obj.name, obj.make_noise()))
60 | A Dog goes woof!
61 | A Cat goes meow!
62 | A Human goes 'hello'
63 | A Car goes vroom!!!
64 | """
65 |
66 | def __init__(self, obj, **adapted_methods):
67 | """We set the adapted methods in the object's dict"""
68 | self.obj = obj
69 | self.__dict__.update(adapted_methods)
70 |
71 | def __getattr__(self, attr):
72 | """All non-adapted calls are passed to the object"""
73 | return getattr(self.obj, attr)
74 |
75 |
76 | def main():
77 | objects = []
78 | dog = Dog()
79 | objects.append(Adapter(dog, make_noise=dog.bark))
80 | cat = Cat()
81 | objects.append(Adapter(cat, make_noise=cat.meow))
82 | human = Human()
83 | objects.append(Adapter(human, make_noise=human.speak))
84 | car = Car()
85 | objects.append(Adapter(car, make_noise=lambda: car.make_noise(3)))
86 |
87 | for obj in objects:
88 | print("A {0} goes {1}".format(obj.name, obj.make_noise()))
89 |
90 |
91 | if __name__ == "__main__":
92 | main()
93 |
94 | ### OUTPUT ###
95 | # A Dog goes woof!
96 | # A Cat goes meow!
97 | # A Human goes 'hello'
98 | # A Car goes vroom!!!
99 |
--------------------------------------------------------------------------------
/graph_search.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | class GraphSearch:
6 |
7 | """Graph search emulation in python, from source
8 | http://www.python.org/doc/essays/graphs/"""
9 |
10 | def __init__(self, graph):
11 | self.graph = graph
12 |
13 | def find_path(self, start, end, path=None):
14 | self.start = start
15 | self.end = end
16 | self.path = path if path else []
17 |
18 | self.path += [self.start]
19 | if self.start == self.end:
20 | return self.path
21 | if self.start not in self.graph:
22 | return None
23 | for node in self.graph[self.start]:
24 | if node not in self.path:
25 | newpath = self.find_path(node, self.end, self.path)
26 | if newpath:
27 | return newpath
28 | return None
29 |
30 | def find_all_path(self, start, end, path=None):
31 | self.start = start
32 | self.end = end
33 | _path = path if path else []
34 | _path += [self.start]
35 | if self.start == self.end:
36 | return [_path]
37 | if self.start not in self.graph:
38 | return []
39 | paths = []
40 | for node in self.graph[self.start]:
41 | if node not in _path:
42 | newpaths = self.find_all_path(node, self.end, _path[:])
43 | for newpath in newpaths:
44 | paths.append(newpath)
45 | return paths
46 |
47 | def find_shortest_path(self, start, end, path=None):
48 | self.start = start
49 | self.end = end
50 | _path = path if path else []
51 |
52 | _path += [self.start]
53 | if self.start == self.end:
54 | return _path
55 | if self.start not in self.graph:
56 | return None
57 | shortest = None
58 | for node in self.graph[self.start]:
59 | if node not in _path:
60 | newpath = self.find_shortest_path(node, self.end, _path[:])
61 | if newpath:
62 | if not shortest or len(newpath) < len(shortest):
63 | shortest = newpath
64 | return shortest
65 |
66 | # example of graph usage
67 | graph = {'A': ['B', 'C'],
68 | 'B': ['C', 'D'],
69 | 'C': ['D'],
70 | 'D': ['C'],
71 | 'E': ['F'],
72 | 'F': ['C']
73 | }
74 |
75 | # initialization of new graph search object
76 | graph1 = GraphSearch(graph)
77 |
78 |
79 | print(graph1.find_path('A', 'D'))
80 | print(graph1.find_all_path('A', 'D'))
81 | print(graph1.find_shortest_path('A', 'D'))
82 |
83 | ### OUTPUT ###
84 | # ['A', 'B', 'C', 'D']
85 | # [['A', 'B', 'C', 'D'], ['A', 'B', 'D'], ['A', 'C', 'D']]
86 | # ['A', 'B', 'D']
87 |
--------------------------------------------------------------------------------
/observer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://code.activestate.com/recipes/131499-observer-pattern/"""
5 |
6 |
7 | class Subject(object):
8 |
9 | def __init__(self):
10 | self._observers = []
11 |
12 | def attach(self, observer):
13 | if observer not in self._observers:
14 | self._observers.append(observer)
15 |
16 | def detach(self, observer):
17 | try:
18 | self._observers.remove(observer)
19 | except ValueError:
20 | pass
21 |
22 | def notify(self, modifier=None):
23 | for observer in self._observers:
24 | if modifier != observer:
25 | observer.update(self)
26 |
27 |
28 | # Example usage
29 | class Data(Subject):
30 |
31 | def __init__(self, name=''):
32 | Subject.__init__(self)
33 | self.name = name
34 | self._data = 0
35 |
36 | @property
37 | def data(self):
38 | return self._data
39 |
40 | @data.setter
41 | def data(self, value):
42 | self._data = value
43 | self.notify()
44 |
45 |
46 | class HexViewer:
47 |
48 | def update(self, subject):
49 | print('HexViewer: Subject %s has data 0x%x' %
50 | (subject.name, subject.data))
51 |
52 |
53 | class DecimalViewer:
54 |
55 | def update(self, subject):
56 | print('DecimalViewer: Subject %s has data %d' %
57 | (subject.name, subject.data))
58 |
59 |
60 | # Example usage...
61 | def main():
62 | data1 = Data('Data 1')
63 | data2 = Data('Data 2')
64 | view1 = DecimalViewer()
65 | view2 = HexViewer()
66 | data1.attach(view1)
67 | data1.attach(view2)
68 | data2.attach(view2)
69 | data2.attach(view1)
70 |
71 | print("Setting Data 1 = 10")
72 | data1.data = 10
73 | print("Setting Data 2 = 15")
74 | data2.data = 15
75 | print("Setting Data 1 = 3")
76 | data1.data = 3
77 | print("Setting Data 2 = 5")
78 | data2.data = 5
79 | print("Detach HexViewer from data1 and data2.")
80 | data1.detach(view2)
81 | data2.detach(view2)
82 | print("Setting Data 1 = 10")
83 | data1.data = 10
84 | print("Setting Data 2 = 15")
85 | data2.data = 15
86 |
87 |
88 | if __name__ == '__main__':
89 | main()
90 |
91 | ### OUTPUT ###
92 | # Setting Data 1 = 10
93 | # DecimalViewer: Subject Data 1 has data 10
94 | # HexViewer: Subject Data 1 has data 0xa
95 | # Setting Data 2 = 15
96 | # HexViewer: Subject Data 2 has data 0xf
97 | # DecimalViewer: Subject Data 2 has data 15
98 | # Setting Data 1 = 3
99 | # DecimalViewer: Subject Data 1 has data 3
100 | # HexViewer: Subject Data 1 has data 0x3
101 | # Setting Data 2 = 5
102 | # HexViewer: Subject Data 2 has data 0x5
103 | # DecimalViewer: Subject Data 2 has data 5
104 | # Detach HexViewer from data1 and data2.
105 | # Setting Data 1 = 10
106 | # DecimalViewer: Subject Data 1 has data 10
107 | # Setting Data 2 = 15
108 | # DecimalViewer: Subject Data 2 has data 15
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | python-patterns
2 | ===============
3 |
4 | A collection of design patterns and idioms in Python.
5 |
6 | When an implementation is added or modified, be sure to update this file and
7 | rerun `append_output.sh` (eg. ./append_output.sh borg.py) to keep the output
8 | comments at the bottom up to date.
9 |
10 | Current Patterns:
11 |
12 | | Pattern | Description |
13 | |:-------:| ----------- |
14 | | [3-tier](3-tier.py) | data<->business logic<->presentation separation (strict relationships) |
15 | | [abstract_factory](abstract_factory.py) | use a generic function with specific factories |
16 | | [adapter](adapter.py) | adapt one interface to another using a whitelist |
17 | | [borg](borg.py) | a singleton with shared-state among instances |
18 | | [bridge](bridge.py) | a client-provider middleman to soften interface changes |
19 | | [builder](builder.py) | call many little discrete methods rather than having a huge number of constructor parameters |
20 | | [catalog](catalog.py) | general methods will call different specialized methods based on construction parameter |
21 | | [chain](chain.py) | apply a chain of successive handlers to try and process the data |
22 | | [chaining_method](chaining_method.py) | continue callback next object method |
23 | | [command](command.py) | bundle a command and arguments to call later |
24 | | [composite](composite.py) | encapsulate and provide access to a number of different objects |
25 | | [decorator](decorator.py) | wrap functionality with other functionality in order to affect outputs |
26 | | [facade](facade.py) | use one class as an API to a number of others |
27 | | [factory_method](factory_method.py) | delegate a specialized function/method to create instances |
28 | | [flyweight](flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
29 | | [graph_search](graph_search.py) | (graphing algorithms, not design patterns) |
30 | | [lazy_evaluation](lazy_evaluation.py) | lazily-evaluated property pattern in Python |
31 | | [mediator](mediator.py) | an object that knows how to connect other objects and act as a proxy |
32 | | [memento](memento.py) | generate an opaque token that can be used to go back to a previous state |
33 | | [mvc](mvc.py) | model<->view<->controller (non-strict relationships) |
34 | | [observer](observer.py) | provide a callback for notification of events/changes to data |
35 | | [pool](pool.py) | preinstantiate and maintain a group of instances of the same type |
36 | | [prototype](prototype.py) | use a factory and clones of a prototype for new instances (if instantiation is expensive) |
37 | | [proxy](proxy.py) | an object funnels operations to something else |
38 | | [publish_subscribe](publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
39 | | [state](state.py) | logic is org'd into a discrete number of potential states and the next state that can be transitioned to |
40 | | [strategy](strategy.py) | selectable operations over the same data |
41 | | [template](template.py) | an object imposes a structure but takes pluggable components |
42 | | [visitor](visitor.py) | invoke a callback for all items of a collection |
43 |
--------------------------------------------------------------------------------
/memento.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://code.activestate.com/recipes/413838-memento-closure/"""
5 |
6 | import copy
7 |
8 |
9 | def Memento(obj, deep=False):
10 | state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
11 |
12 | def Restore():
13 | obj.__dict__.clear()
14 | obj.__dict__.update(state)
15 | return Restore
16 |
17 |
18 | class Transaction:
19 |
20 | """A transaction guard. This is really just
21 | syntactic suggar arount a memento closure.
22 | """
23 | deep = False
24 |
25 | def __init__(self, *targets):
26 | self.targets = targets
27 | self.Commit()
28 |
29 | def Commit(self):
30 | self.states = [Memento(target, self.deep) for target in self.targets]
31 |
32 | def Rollback(self):
33 | for st in self.states:
34 | st()
35 |
36 |
37 | class transactional(object):
38 |
39 | """Adds transactional semantics to methods. Methods decorated with
40 | @transactional will rollback to entry state upon exceptions.
41 | """
42 |
43 | def __init__(self, method):
44 | self.method = method
45 |
46 | def __get__(self, obj, T):
47 | def transaction(*args, **kwargs):
48 | state = Memento(obj)
49 | try:
50 | return self.method(obj, *args, **kwargs)
51 | except:
52 | state()
53 | raise
54 | return transaction
55 |
56 |
57 | class NumObj(object):
58 |
59 | def __init__(self, value):
60 | self.value = value
61 |
62 | def __repr__(self):
63 | return '<%s: %r>' % (self.__class__.__name__, self.value)
64 |
65 | def Increment(self):
66 | self.value += 1
67 |
68 | @transactional
69 | def DoStuff(self):
70 | self.value = '1111' # <- invalid value
71 | self.Increment() # <- will fail and rollback
72 |
73 |
74 | if __name__ == '__main__':
75 | n = NumObj(-1)
76 | print(n)
77 | t = Transaction(n)
78 | try:
79 | for i in range(3):
80 | n.Increment()
81 | print(n)
82 | t.Commit()
83 | print('-- commited')
84 | for i in range(3):
85 | n.Increment()
86 | print(n)
87 | n.value += 'x' # will fail
88 | print(n)
89 | except:
90 | t.Rollback()
91 | print('-- rolled back')
92 | print(n)
93 | print('-- now doing stuff ...')
94 | try:
95 | n.DoStuff()
96 | except:
97 | print('-> doing stuff failed!')
98 | import sys
99 | import traceback
100 | traceback.print_exc(file=sys.stdout)
101 | pass
102 | print(n)
103 |
104 | ### OUTPUT ###
105 | #
106 | #
107 | #
108 | #
109 | # -- commited
110 | #
111 | #
112 | #
113 | # -- rolled back
114 | #
115 | # -- now doing stuff ...
116 | # -> doing stuff failed!
117 | # Traceback (most recent call last):
118 | # File "memento.py", line 91, in
119 | # n.DoStuff()
120 | # File "memento.py", line 47, in transaction
121 | # return self.method(obj, *args, **kwargs)
122 | # File "memento.py", line 67, in DoStuff
123 | # self.Increment() # <- will fail and rollback
124 | # File "memento.py", line 62, in Increment
125 | # self.value += 1
126 | # TypeError: Can't convert 'int' object to str implicitly
127 | #
128 |
--------------------------------------------------------------------------------
/mediator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """http://dpip.testingperspective.com/?p=28"""
5 |
6 | import random
7 | import time
8 |
9 |
10 | class TC:
11 |
12 | def __init__(self):
13 | self._tm = None
14 | self._bProblem = 0
15 |
16 | def setup(self):
17 | print("Setting up the Test")
18 | time.sleep(0.1)
19 | self._tm.prepareReporting()
20 |
21 | def execute(self):
22 | if not self._bProblem:
23 | print("Executing the test")
24 | time.sleep(0.1)
25 | else:
26 | print("Problem in setup. Test not executed.")
27 |
28 | def tearDown(self):
29 | if not self._bProblem:
30 | print("Tearing down")
31 | time.sleep(0.1)
32 | self._tm.publishReport()
33 | else:
34 | print("Test not executed. No tear down required.")
35 |
36 | def setTM(self, tm):
37 | self._tm = tm
38 |
39 | def setProblem(self, value):
40 | self._bProblem = value
41 |
42 |
43 | class Reporter:
44 |
45 | def __init__(self):
46 | self._tm = None
47 |
48 | def prepare(self):
49 | print("Reporter Class is preparing to report the results")
50 | time.sleep(0.1)
51 |
52 | def report(self):
53 | print("Reporting the results of Test")
54 | time.sleep(0.1)
55 |
56 | def setTM(self, tm):
57 | self._tm = tm
58 |
59 |
60 | class DB:
61 |
62 | def __init__(self):
63 | self._tm = None
64 |
65 | def insert(self):
66 | print("Inserting the execution begin status in the Database")
67 | time.sleep(0.1)
68 | # Following code is to simulate a communication from DB to TC
69 | if random.randrange(1, 4) == 3:
70 | return -1
71 |
72 | def update(self):
73 | print("Updating the test results in Database")
74 | time.sleep(0.1)
75 |
76 | def setTM(self, tm):
77 | self._tm = tm
78 |
79 |
80 | class TestManager:
81 |
82 | def __init__(self):
83 | self._reporter = None
84 | self._db = None
85 | self._tc = None
86 |
87 | def prepareReporting(self):
88 | rvalue = self._db.insert()
89 | if rvalue == -1:
90 | self._tc.setProblem(1)
91 | self._reporter.prepare()
92 |
93 | def setReporter(self, reporter):
94 | self._reporter = reporter
95 |
96 | def setDB(self, db):
97 | self._db = db
98 |
99 | def publishReport(self):
100 | self._db.update()
101 | self._reporter.report()
102 |
103 | def setTC(self, tc):
104 | self._tc = tc
105 |
106 |
107 | if __name__ == '__main__':
108 | reporter = Reporter()
109 | db = DB()
110 | tm = TestManager()
111 | tm.setReporter(reporter)
112 | tm.setDB(db)
113 | reporter.setTM(tm)
114 | db.setTM(tm)
115 | # For simplification we are looping on the same test.
116 | # Practically, it could be about various unique test classes and their
117 | # objects
118 | for i in range(3):
119 | tc = TC()
120 | tc.setTM(tm)
121 | tm.setTC(tc)
122 | tc.setup()
123 | tc.execute()
124 | tc.tearDown()
125 |
126 | ### OUTPUT ###
127 | # Setting up the Test
128 | # Inserting the execution begin status in the Database
129 | # Executing the test
130 | # Tearing down
131 | # Updating the test results in Database
132 | # Reporting the results of Test
133 | # Setting up the Test
134 | # Inserting the execution begin status in the Database
135 | # Reporter Class is preparing to report the results
136 | # Problem in setup. Test not executed.
137 | # Test not executed. No tear down required.
138 | # Setting up the Test
139 | # Inserting the execution begin status in the Database
140 | # Executing the test
141 | # Tearing down
142 | # Updating the test results in Database
143 | # Reporting the results of Test
144 |
--------------------------------------------------------------------------------
/composite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | A class which defines a composite object which can store
6 | hieararchical dictionaries with names.
7 |
8 | This class is same as a hiearchical dictionary, but it
9 | provides methods to add/access/modify children by name,
10 | like a Composite.
11 |
12 | Created Anand B Pillai
13 |
14 | """
15 | __author__ = "Anand B Pillai"
16 | __maintainer__ = "Anand B Pillai"
17 | __version__ = "0.2"
18 |
19 |
20 | def normalize(val):
21 | """ Normalize a string so that it can be used as an attribute
22 | to a Python object """
23 |
24 | if val.find('-') != -1:
25 | val = val.replace('-', '_')
26 |
27 | return val
28 |
29 |
30 | def denormalize(val):
31 | """ De-normalize a string """
32 |
33 | if val.find('_') != -1:
34 | val = val.replace('_', '-')
35 |
36 | return val
37 |
38 |
39 | class SpecialDict(dict):
40 |
41 | """ A dictionary type which allows direct attribute
42 | access to its keys """
43 |
44 | def __getattr__(self, name):
45 |
46 | if name in self.__dict__:
47 | return self.__dict__[name]
48 | elif name in self:
49 | return self.get(name)
50 | else:
51 | # Check for denormalized name
52 | name = denormalize(name)
53 | if name in self:
54 | return self.get(name)
55 | else:
56 | raise AttributeError('no attribute named %s' % name)
57 |
58 | def __setattr__(self, name, value):
59 |
60 | if name in self.__dict__:
61 | self.__dict__[name] = value
62 | elif name in self:
63 | self[name] = value
64 | else:
65 | # Check for denormalized name
66 | name2 = denormalize(name)
67 | if name2 in self:
68 | self[name2] = value
69 | else:
70 | # New attribute
71 | self[name] = value
72 |
73 |
74 | class CompositeDict(SpecialDict):
75 |
76 | """ A class which works like a hierarchical dictionary.
77 | This class is based on the Composite design-pattern """
78 |
79 | ID = 0
80 |
81 | def __init__(self, name=''):
82 |
83 | if name:
84 | self._name = name
85 | else:
86 | self._name = ''.join(('id#', str(self.__class__.ID)))
87 | self.__class__.ID += 1
88 |
89 | self._children = []
90 | # Link back to father
91 | self._father = None
92 | self[self._name] = SpecialDict()
93 |
94 | def __getattr__(self, name):
95 |
96 | if name in self.__dict__:
97 | return self.__dict__[name]
98 | elif name in self:
99 | return self.get(name)
100 | else:
101 | # Check for denormalized name
102 | name = denormalize(name)
103 | if name in self:
104 | return self.get(name)
105 | else:
106 | # Look in children list
107 | child = self.findChild(name)
108 | if child:
109 | return child
110 | else:
111 | attr = getattr(self[self._name], name)
112 | if attr:
113 | return attr
114 |
115 | raise AttributeError('no attribute named %s' % name)
116 |
117 | def isRoot(self):
118 | """ Return whether I am a root component or not """
119 |
120 | # If I don't have a parent, I am root
121 | return not self._father
122 |
123 | def isLeaf(self):
124 | """ Return whether I am a leaf component or not """
125 |
126 | # I am a leaf if I have no children
127 | return not self._children
128 |
129 | def getName(self):
130 | """ Return the name of this ConfigInfo object """
131 |
132 | return self._name
133 |
134 | def getIndex(self, child):
135 | """ Return the index of the child ConfigInfo object 'child' """
136 |
137 | if child in self._children:
138 | return self._children.index(child)
139 | else:
140 | return -1
141 |
142 | def getDict(self):
143 | """ Return the contained dictionary """
144 |
145 | return self[self._name]
146 |
147 | def getProperty(self, child, key):
148 | """ Return the value for the property for child
149 | 'child' with key 'key' """
150 |
151 | # First get the child's dictionary
152 | childDict = self.getInfoDict(child)
153 | if childDict:
154 | return childDict.get(key, None)
155 |
156 | def setProperty(self, child, key, value):
157 | """ Set the value for the property 'key' for
158 | the child 'child' to 'value' """
159 |
160 | # First get the child's dictionary
161 | childDict = self.getInfoDict(child)
162 | if childDict:
163 | childDict[key] = value
164 |
165 | def getChildren(self):
166 | """ Return the list of immediate children of this object """
167 |
168 | return self._children
169 |
170 | def getAllChildren(self):
171 | """ Return the list of all children of this object """
172 |
173 | l = []
174 | for child in self._children:
175 | l.append(child)
176 | l.extend(child.getAllChildren())
177 |
178 | return l
179 |
180 | def getChild(self, name):
181 | """ Return the immediate child object with the given name """
182 |
183 | for child in self._children:
184 | if child.getName() == name:
185 | return child
186 |
187 | def findChild(self, name):
188 | """ Return the child with the given name from the tree """
189 |
190 | # Note - this returns the first child of the given name
191 | # any other children with similar names down the tree
192 | # is not considered.
193 |
194 | for child in self.getAllChildren():
195 | if child.getName() == name:
196 | return child
197 |
198 | def findChildren(self, name):
199 | """ Return a list of children with the given name from the tree """
200 |
201 | # Note: this returns a list of all the children of a given
202 | # name, irrespective of the depth of look-up.
203 |
204 | children = []
205 |
206 | for child in self.getAllChildren():
207 | if child.getName() == name:
208 | children.append(child)
209 |
210 | return children
211 |
212 | def getPropertyDict(self):
213 | """ Return the property dictionary """
214 |
215 | d = self.getChild('__properties')
216 | if d:
217 | return d.getDict()
218 | else:
219 | return {}
220 |
221 | def getParent(self):
222 | """ Return the person who created me """
223 |
224 | return self._father
225 |
226 | def __setChildDict(self, child):
227 | """ Private method to set the dictionary of the child
228 | object 'child' in the internal dictionary """
229 |
230 | d = self[self._name]
231 | d[child.getName()] = child.getDict()
232 |
233 | def setParent(self, father):
234 | """ Set the parent object of myself """
235 |
236 | # This should be ideally called only once
237 | # by the father when creating the child :-)
238 | # though it is possible to change parenthood
239 | # when a new child is adopted in the place
240 | # of an existing one - in that case the existing
241 | # child is orphaned - see addChild and addChild2
242 | # methods !
243 | self._father = father
244 |
245 | def setName(self, name):
246 | """ Set the name of this ConfigInfo object to 'name' """
247 |
248 | self._name = name
249 |
250 | def setDict(self, d):
251 | """ Set the contained dictionary """
252 |
253 | self[self._name] = d.copy()
254 |
255 | def setAttribute(self, name, value):
256 | """ Set a name value pair in the contained dictionary """
257 |
258 | self[self._name][name] = value
259 |
260 | def getAttribute(self, name):
261 | """ Return value of an attribute from the contained dictionary """
262 |
263 | return self[self._name][name]
264 |
265 | def addChild(self, name, force=False):
266 | """ Add a new child 'child' with the name 'name'.
267 | If the optional flag 'force' is set to True, the
268 | child object is overwritten if it is already there.
269 |
270 | This function returns the child object, whether
271 | new or existing """
272 |
273 | if type(name) != str:
274 | raise ValueError('Argument should be a string!')
275 |
276 | child = self.getChild(name)
277 | if child:
278 | # print('Child %s present!' % name)
279 | # Replace it if force==True
280 | if force:
281 | index = self.getIndex(child)
282 | if index != -1:
283 | child = self.__class__(name)
284 | self._children[index] = child
285 | child.setParent(self)
286 |
287 | self.__setChildDict(child)
288 | return child
289 | else:
290 | child = self.__class__(name)
291 | child.setParent(self)
292 |
293 | self._children.append(child)
294 | self.__setChildDict(child)
295 |
296 | return child
297 |
298 | def addChild2(self, child):
299 | """ Add the child object 'child'. If it is already present,
300 | it is overwritten by default """
301 |
302 | currChild = self.getChild(child.getName())
303 | if currChild:
304 | index = self.getIndex(currChild)
305 | if index != -1:
306 | self._children[index] = child
307 | child.setParent(self)
308 | # Unset the existing child's parent
309 | currChild.setParent(None)
310 | del currChild
311 |
312 | self.__setChildDict(child)
313 | else:
314 | child.setParent(self)
315 | self._children.append(child)
316 | self.__setChildDict(child)
317 |
318 |
319 | if __name__ == "__main__":
320 | window = CompositeDict('Window')
321 | frame = window.addChild('Frame')
322 | tfield = frame.addChild('Text Field')
323 | tfield.setAttribute('size', '20')
324 |
325 | btn = frame.addChild('Button1')
326 | btn.setAttribute('label', 'Submit')
327 |
328 | btn = frame.addChild('Button2')
329 | btn.setAttribute('label', 'Browse')
330 |
331 | # print(window)
332 | # print(window.Frame)
333 | # print(window.Frame.Button1)
334 | # print(window.Frame.Button2)
335 | print(window.Frame.Button1.label)
336 | print(window.Frame.Button2.label)
337 |
338 | ### OUTPUT ###
339 | # Submit
340 | # Browse
341 |
--------------------------------------------------------------------------------