Browse Source

Merge pull request #1 from faif/master

Update from original
Ibrahim Diop 11 năm trước cách đây
mục cha
commit
640cf76b32
37 tập tin đã thay đổi với 773 bổ sung327 xóa
  1. 1 0
      .gitignore
  2. 26 5
      3-tier.py
  3. 35 28
      README.md
  4. BIN
      __pycache__/facade.cpython-33.pyc
  5. BIN
      __pycache__/mediator.cpython-33.pyc
  6. BIN
      __pycache__/null.cpython-33.pyc
  7. 27 10
      abstract_factory.py
  8. 13 10
      adapter.py
  9. 12 0
      append_output.sh
  10. 25 9
      borg.py
  11. 15 5
      bridge.py
  12. 10 1
      builder.py
  13. 17 6
      catalog.py
  14. 40 26
      chain.py
  15. 33 0
      chaining_method.py
  16. 11 4
      command.py
  17. 45 36
      composite.py
  18. 22 35
      decorator.py
  19. 26 1
      facade.py
  20. 15 3
      factory_method.py
  21. 9 0
      flyweight.py
  22. 0 1
      foo.txt
  23. 37 27
      graph_search.py
  24. 9 0
      iterator.py
  25. 35 10
      mediator.py
  26. 35 2
      memento.py
  27. 17 0
      mvc.py
  28. 0 81
      null.py
  29. 28 2
      observer.py
  30. 10 0
      pool.py
  31. 22 9
      prototype.py
  32. 24 13
      proxy.py
  33. 92 0
      publish_subscribe.py
  34. 17 0
      state.py
  35. 6 0
      strategy.py
  36. 50 0
      template.py
  37. 9 3
      visitor.py

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+__pycache__

+ 26 - 5
3-tier.py

@@ -3,6 +3,7 @@
 
 
 class Data(object):
+    """ Data Store Class """
 
     products = {
         'milk': {'price': 1.50, 'quantity': 10},
@@ -10,20 +11,26 @@ class Data(object):
         'cheese': {'price': 2.00, 'quantity': 10}
     }
 
+    def __get__(self, obj, klas):
+        print ("(Fetching from Data Store)")
+        return {'products': self.products}
+
 
 class BusinessLogic(object):
 
-    def __init__(self):
-        self.data = Data()
+    """ Business logic holding data store instances """
+
+    data = Data()
 
     def product_list(self):
-        return self.data.products.keys()
+        return self.data['products'].keys()
 
     def product_information(self, product):
-        return self.data.products.get(product, None)
+        return self.data['products'].get(product, None)
 
 
 class Ui(object):
+    """ UI interaction class """
 
     def __init__(self):
         self.business_logic = BusinessLogic()
@@ -31,7 +38,8 @@ class Ui(object):
     def get_product_list(self):
         print('PRODUCT LIST:')
         for product in self.business_logic.product_list():
-            print(product)
+            #print(product)
+            yield product
         print('')
 
     def get_product_information(self, product):
@@ -56,3 +64,16 @@ def main():
 
 if __name__ == '__main__':
     main()
+
+### OUTPUT ###
+# (Fetching from Data Store)
+# PRODUCT INFORMATION:
+# Name: Cheese, Price: 2.00, Quantity: 10
+# (Fetching from Data Store)
+# PRODUCT INFORMATION:
+# Name: Eggs, Price: 0.20, Quantity: 100
+# (Fetching from Data Store)
+# PRODUCT INFORMATION:
+# Name: Milk, Price: 1.50, Quantity: 10
+# (Fetching from Data Store)
+# That product "arepas" does not exist in the records

+ 35 - 28
README.md

@@ -1,34 +1,41 @@
 python-patterns
 ===============
 
-A collection of design patterns implemented (by other people) in python
+A collection of design patterns and idioms in Python.
+
+When an implementation is added or modified, be sure to update this file and
+rerun `append_output.sh` (eg. ./append_output.sh borg.py) to keep the output
+comments at the bottom up to date.
 
 Current Patterns:
 
-* 3-tier		
-* composite		
-* mvc
-* decorator		
-* null
-* facade		
-* observer
-* abstract_factory	
-* factory_method	
-* pool
-* adapter		
-* flyweight		
-* prototype
-* borg					
-* proxy
-* bridge		
-* graph_search		
-* state
-* builder		
-* iterator		
-* strategy
-* chain		
-* mediator		
-* template
-* command		
-* memento		
-* visitor
+| Pattern | Description |
+|:-------:| ----------- |
+| [3-tier](3-tier.py) | data<->business logic<->presentation separation (strict relationships) |
+| [abstract_factory](abstract_factory.py) | use a generic function with specific factories |
+| [adapter](adapter.py) | adapt one interface to another using a whitelist |
+| [borg](borg.py) | a singleton with shared-state among instances |
+| [bridge](bridge.py) | a client-provider middleman to soften interface changes |
+| [builder](builder.py) | call many little discrete methods rather than having a huge number of constructor parameters |
+| [catalog](catalog.py) | general methods will call different specialized methods based on construction parameter |
+| [chain](chain.py) | apply a chain of successive handlers to try and process the data |
+| [command](command.py) | bundle a command and arguments to call later |
+| [composite](composite.py) | encapsulate and provide access to a number of different objects |
+| [decorator](decorator.py) | wrap functionality with other functionality in order to affect outputs |
+| [facade](facade.py) | use one class as an API to a number of others |
+| [factory_method](factory_method.py) | delegate a specialized function/method to create instances |
+| [flyweight](flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
+| [graph_search](graph_search.py) | (graphing algorithms, not design patterns) |
+| [mediator](mediator.py) | an object that knows how to connect other objects and act as a proxy |
+| [memento](memento.py) | generate an opaque token that can be used to go back to a previous state |
+| [mvc](mvc.py) | model<->view<->controller (non-strict relationships) |
+| [observer](observer.py) | provide a callback for notification of events/changes to data |
+| [pool](pool.py) | preinstantiate and maintain a group of instances of the same type |
+| [prototype](prototype.py) | use a factory and clones of a prototype for new instances (if instantiation is expensive) |
+| [proxy](proxy.py) | an object funnels operations to something else |
+| [publish_subscribe](publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
+| [state](state.py) | logic is org'd into a discrete number of potential states and the next state that can be transitioned to |
+| [strategy](strategy.py) | selectable operations over the same data |
+| [template](template.py) | an object imposes a structure but takes pluggable components |
+| [visitor](visitor.py) | invoke a callback for all items of a collection |
+| [chaining_method](chaining_method.py) | continue callback next object method |

BIN
__pycache__/facade.cpython-33.pyc


BIN
__pycache__/mediator.cpython-33.pyc


BIN
__pycache__/null.cpython-33.pyc


+ 27 - 10
abstract_factory.py

@@ -1,32 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 # http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
 
 """Implementation of the abstract factory pattern"""
 
 import random
 
-
 class PetShop:
+
     """A pet shop"""
 
     def __init__(self, animal_factory=None):
-        """pet_factory is our abstract factory.
-        We can set it at will."""
+        """pet_factory is our abstract factory.  We can set it at will."""
 
         self.pet_factory = animal_factory
 
     def show_pet(self):
-        """Creates and shows a pet using the
-        abstract factory"""
+        """Creates and shows a pet using the abstract factory"""
 
         pet = self.pet_factory.get_pet()
-        print("This is a lovely {}".format(pet))
+        print("We have a lovely {}".format(pet))
         print("It says {}".format(pet.speak()))
-        print("It eats {}".format(self.pet_factory.get_food()))
+        print("We also have {}".format(self.pet_factory.get_food()))
 
 
 # Stuff that our factory makes
 
 class Dog:
+
     def speak(self):
         return "woof"
 
@@ -35,6 +37,7 @@ class Dog:
 
 
 class Cat:
+
     def speak(self):
         return "meow"
 
@@ -45,6 +48,7 @@ class Cat:
 # Factory classes
 
 class DogFactory:
+
     def get_pet(self):
         return Dog()
 
@@ -53,13 +57,13 @@ class DogFactory:
 
 
 class CatFactory:
+
     def get_pet(self):
         return Cat()
 
     def get_food(self):
         return "cat food"
 
-
 # Create the proper family
 def get_factory():
     """Let's be dynamic!"""
@@ -68,8 +72,21 @@ def get_factory():
 
 # Show pets with various factories
 if __name__ == "__main__":
-    shop = PetShop()
     for i in range(3):
-        shop.pet_factory = get_factory()
+        shop = PetShop(get_factory())
         shop.show_pet()
         print("=" * 20)
+
+### OUTPUT ###
+# We have a lovely Dog
+# It says woof
+# We also have dog food
+# ====================
+# We have a lovely Dog
+# It says woof
+# We also have dog food
+# ====================
+# We have a lovely Cat
+# It says meow
+# We also have cat food
+# ====================

+ 13 - 10
adapter.py

@@ -1,28 +1,25 @@
-# http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
 
-import os
+"""http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/"""
 
+import os
 
 class Dog(object):
     def __init__(self):
         self.name = "Dog"
-
     def bark(self):
         return "woof!"
 
-
 class Cat(object):
     def __init__(self):
         self.name = "Cat"
-
     def meow(self):
         return "meow!"
 
-
 class Human(object):
     def __init__(self):
         self.name = "Human"
-
     def speak(self):
         return "'hello'"
 
@@ -30,12 +27,12 @@ class Human(object):
 class Car(object):
     def __init__(self):
         self.name = "Car"
-
     def make_noise(self, octane_level):
         return "vroom{0}".format("!" * octane_level)
 
 
 class Adapter(object):
+
     """
     Adapts an object by replacing methods.
     Usage:
@@ -60,6 +57,7 @@ class Adapter(object):
     A Human goes 'hello'
     A Car goes vroom!!!
     """
+
     def __init__(self, obj, adapted_methods):
         """We set the adapted methods in the object's dict"""
         self.obj = obj
@@ -86,5 +84,10 @@ def main():
 
 
 if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
+    main()
+
+### OUTPUT ###
+# A Dog goes woof!
+# A Cat goes meow!
+# A Human goes 'hello'
+# A Car goes vroom!!!

+ 12 - 0
append_output.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+src=$(sed -n -e '/### OUTPUT ###/,$!p' "$1")
+output=$(python "$1" | sed 's/^/# /')
+
+# These are done separately to avoid having to insert a newline, which causes
+# problems when the text itself has '\n' in strings
+echo "$src" > $1
+echo -e "\n### OUTPUT ###" >> $1
+echo "$output" >> $1

+ 25 - 9
borg.py

@@ -1,8 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
 class Borg:
     __shared_state = {}
 
     def __init__(self):
         self.__dict__ = self.__shared_state
+        self.state = 'Init'
 
     def __str__(self):
         return self.state
@@ -18,19 +23,30 @@ if __name__ == '__main__':
     rm1.state = 'Idle'
     rm2.state = 'Running'
 
-    print('rm1:', rm1)
-    print('rm2:', rm2)
+    print('rm1: {0}'.format(rm1))
+    print('rm2: {0}'.format(rm2))
 
     rm2.state = 'Zombie'
 
-    print('rm1:', rm1)
-    print('rm2:', rm2)
+    print('rm1: {0}'.format(rm1))
+    print('rm2: {0}'.format(rm2))
 
-    print('rm1 id:', id(rm1))
-    print('rm2 id:', id(rm2))
+    print('rm1 id: {0}'.format(id(rm1)))
+    print('rm2 id: {0}'.format(id(rm2)))
 
     rm3 = YourBorg()
 
-    print('rm1:', rm1)
-    print('rm2:', rm2)
-    print('rm3:', rm3)
+    print('rm1: {0}'.format(rm1))
+    print('rm2: {0}'.format(rm2))
+    print('rm3: {0}'.format(rm3))
+
+### OUTPUT ###
+# rm1: Running
+# rm2: Running
+# rm1: Zombie
+# rm2: Zombie
+# rm1 id: 140732837899224
+# rm2 id: 140732837899296
+# rm1: Init
+# rm2: Init
+# rm3: Init

+ 15 - 5
bridge.py

@@ -1,30 +1,36 @@
-# http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Bridge_Pattern#Python
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""http://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Bridge_Pattern#Python"""
 
 
 # ConcreteImplementor 1/2
 class DrawingAPI1(object):
+
     def draw_circle(self, x, y, radius):
         print('API1.circle at {}:{} radius {}'.format(x, y, radius))
 
 
 # ConcreteImplementor 2/2
 class DrawingAPI2(object):
+
     def draw_circle(self, x, y, radius):
         print('API2.circle at {}:{} radius {}'.format(x, y, radius))
 
 
 # Refined Abstraction
 class CircleShape(object):
+
     def __init__(self, x, y, radius, drawing_api):
         self._x = x
         self._y = y
         self._radius = radius
         self._drawing_api = drawing_api
- 
+
     # low-level i.e. Implementation specific
     def draw(self):
         self._drawing_api.draw_circle(self._x, self._y, self._radius)
- 
+
     # high-level i.e. Abstraction specific
     def scale(self, pct):
         self._radius *= pct
@@ -35,11 +41,15 @@ def main():
         CircleShape(1, 2, 3, DrawingAPI1()),
         CircleShape(5, 7, 11, DrawingAPI2())
     )
- 
+
     for shape in shapes:
         shape.scale(2.5)
         shape.draw()
- 
+
 
 if __name__ == '__main__':
     main()
+
+### OUTPUT ###
+# API1.circle at 1:2 radius 7.5
+# API2.circle at 5:7 radius 27.5

+ 10 - 1
builder.py

@@ -9,6 +9,7 @@ https://gist.github.com/420905#file_builder_python.py
 
 # Director
 class Director(object):
+
     def __init__(self):
         self.builder = None
 
@@ -23,6 +24,7 @@ class Director(object):
 
 # Abstract Builder
 class Builder(object):
+
     def __init__(self):
         self.building = None
 
@@ -32,6 +34,7 @@ class Builder(object):
 
 # Concrete Builder
 class BuilderHouse(Builder):
+
     def build_floor(self):
         self.building.floor = 'One'
 
@@ -40,15 +43,17 @@ class BuilderHouse(Builder):
 
 
 class BuilderFlat(Builder):
+
     def build_floor(self):
         self.building.floor = 'More than One'
-        
+
     def build_size(self):
         self.building.size = 'Small'
 
 
 # Product
 class Building(object):
+
     def __init__(self):
         self.floor = None
         self.size = None
@@ -68,3 +73,7 @@ if __name__ == "__main__":
     director.construct_building()
     building = director.get_building()
     print(building)
+
+### OUTPUT ###
+# Floor: One | Size: Big
+# Floor: More than One | Size: Small

+ 17 - 6
catalog.py

@@ -1,20 +1,28 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """
-A class that uses different static function depending of a parameter passed in init
-Note the use of a single dictionnary instead of multiple conditions
+A class that uses different static function depending of a parameter passed in
+init. Note the use of a single dictionnary instead of multiple conditions
 """
 __author__ = "Ibrahim Diop <http://ibrahim.zinaria.com>"
 __gist__ = "<https://gist.github.com/diopib/7679559>"
 
+
 class Catalog():
+
     """
-    catalog of multiple static methods that are executed depending on an init parameter
+    catalog of multiple static methods that are executed depending on an init
+    parameter
     """
 
     def __init__(self, param):
 
-        # dictionary that will be used to determine which static method is to be executed but
-        # that will be also used to store possible param value
-        self.static_method_choices = {'param_value_1': self.static_method_1, 'param_value_2': self.static_method_2}
+        # dictionary that will be used to determine which static method is
+        # to be executed but that will be also used to store possible param
+        # value
+        self.static_method_choices = {'param_value_1': self.static_method_1,
+                                      'param_value_2': self.static_method_2}
 
         # simple test to validate param value
         if param in self.static_method_choices.keys():
@@ -51,3 +59,6 @@ def main():
 
 if __name__ == "__main__":
     main()
+
+### OUTPUT ###
+# executed method 2!

+ 40 - 26
chain.py

@@ -1,52 +1,66 @@
-# http://www.testingperspective.com/wiki/doku.php/collaboration/chetan/designpatternsinpython/chain-of-responsibilitypattern
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
 
+"""http://www.testingperspective.com/wiki/doku.php/collaboration/chetan/designpatternsinpython/chain-of-responsibilitypattern"""
 
 class Handler:
-    def successor(self, successor):
-        self.successor = successor
+    def __init__(self,successor):
+        self._successor = successor;
+    def handle(self,request):
+        i = self._handle(request)
+        if  not i:
+            self._successor.handle(request)
+    def _handle(self, request):
+        raise NotImplementedError('Must provide implementation in subclass.')
 
 
 class ConcreteHandler1(Handler):
-    def handle(self, request):
+
+    def _handle(self, request):
         if 0 < request <= 10:
             print('request {} handled in handler 1'.format(request))
-        else:
-            self.successor.handle(request)
-
-
+            return True
+            
 class ConcreteHandler2(Handler):
-    def handle(self, request):
+    
+    def _handle(self, request):
         if 10 < request <= 20:
             print('request {} handled in handler 2'.format(request))
-        else:
-            self.successor.handle(request)
-
-
+            return True
+        
 class ConcreteHandler3(Handler):
-    def handle(self, request):
+    
+    def _handle(self, request):
         if 20 < request <= 30:
             print('request {} handled in handler 3'.format(request))
-        else:
-            print('end of chain, no handler for {}'.format(request))
+            return True
+class DefaultHandler(Handler):
+    
+    def _handle(self, request):
+        print('end of chain, no handler for {}'.format(request))
+        return True
 
 
 class Client:
     def __init__(self):
-        h1 = ConcreteHandler1()
-        h2 = ConcreteHandler2()
-        h3 = ConcreteHandler3()
-
-        h1.successor(h2)
-        h2.successor(h3)
-
-        self.handlers = (h1, h2, h3)
-
+        self.handler = ConcreteHandler1(ConcreteHandler3(ConcreteHandler2(DefaultHandler(None))))
     def delegate(self, requests):
         for request in requests:
-            self.handlers[0].handle(request)
+            self.handler.handle(request)
 
 
 if __name__ == "__main__":
     client = Client()
     requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
     client.delegate(requests)
+
+### OUTPUT ###
+# request 2 handled in handler 1
+# request 5 handled in handler 1
+# request 14 handled in handler 2
+# request 22 handled in handler 3
+# request 18 handled in handler 2
+# request 3 handled in handler 1
+# end of chain, no handler for 35
+# request 27 handled in handler 3
+# request 20 handled in handler 2

+ 33 - 0
chaining_method.py

@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+class Person(object):
+
+    def __init__(self, name, action):
+        self.name = name
+        self.action = action
+
+    def do_action(self):
+        print(self.name, self.action.name, end=' ')
+        return self.action
+
+class Action(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def amount(self, val):
+        print(val, end=' ')
+        return self
+
+    def stop(self):
+        print('then stop')
+
+if __name__ == '__main__':
+
+    move = Action('move')
+    person = Person('Jack', move)
+    person.do_action().amount('5m').stop()
+
+### OUTPUT ###
+# Jack move 5m then stop

+ 11 - 4
command.py

@@ -1,15 +1,16 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 import os
 
 
 class MoveFileCommand(object):
+
     def __init__(self, src, dest):
         self.src = src
         self.dest = dest
 
     def execute(self):
-        self()
-
-    def __call__(self):
         print('renaming {} to {}'.format(self.src, self.dest))
         os.rename(self.src, self.dest)
 
@@ -34,4 +35,10 @@ def main():
         cmd.undo()
 
 if __name__ == "__main__":
-    main()
+    main()
+
+### OUTPUT ###
+# renaming foo.txt to bar.txt
+# renaming bar.txt to baz.txt
+# renaming baz.txt to bar.txt
+# renaming bar.txt to foo.txt

+ 45 - 36
composite.py

@@ -1,9 +1,12 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """
 A class which defines a composite object which can store
 hieararchical dictionaries with names.
 
 This class is same as a hiearchical dictionary, but it
-provides methods to add/access/modify children by name, 
+provides methods to add/access/modify children by name,
 like a Composite.
 
 Created Anand B Pillai     <abpillai@gmail.com>
@@ -17,7 +20,7 @@ __version__ = "0.2"
 def normalize(val):
     """ Normalize a string so that it can be used as an attribute
     to a Python object """
-    
+
     if val.find('-') != -1:
         val = val.replace('-', '_')
 
@@ -34,6 +37,7 @@ def denormalize(val):
 
 
 class SpecialDict(dict):
+
     """ A dictionary type which allows direct attribute
     access to its keys """
 
@@ -65,9 +69,10 @@ class SpecialDict(dict):
             else:
                 # New attribute
                 self[name] = value
-        
+
 
 class CompositeDict(SpecialDict):
+
     """ A class which works like a hierarchical dictionary.
     This class is based on the Composite design-pattern """
 
@@ -106,7 +111,7 @@ class CompositeDict(SpecialDict):
                     attr = getattr(self[self._name], name)
                     if attr:
                         return attr
-                    
+
                     raise AttributeError('no attribute named %s' % name)
 
     def isRoot(self):
@@ -114,7 +119,7 @@ class CompositeDict(SpecialDict):
 
         # If I don't have a parent, I am root
         return not self._father
-    
+
     def isLeaf(self):
         """ Return whether I am a leaf component or not """
 
@@ -128,7 +133,7 @@ class CompositeDict(SpecialDict):
 
     def getIndex(self, child):
         """ Return the index of the child ConfigInfo object 'child' """
-        
+
         if child in self._children:
             return self._children.index(child)
         else:
@@ -136,7 +141,7 @@ class CompositeDict(SpecialDict):
 
     def getDict(self):
         """ Return the contained dictionary """
-        
+
         return self[self._name]
 
     def getProperty(self, child, key):
@@ -156,25 +161,25 @@ class CompositeDict(SpecialDict):
         childDict = self.getInfoDict(child)
         if childDict:
             childDict[key] = value
-    
+
     def getChildren(self):
         """ Return the list of immediate children of this object """
-        
+
         return self._children
 
     def getAllChildren(self):
         """ Return the list of all children of this object """
-        
+
         l = []
         for child in self._children:
             l.append(child)
             l.extend(child.getAllChildren())
-            
+
         return l
 
     def getChild(self, name):
         """ Return the immediate child object with the given name """
-        
+
         for child in self._children:
             if child.getName() == name:
                 return child
@@ -185,7 +190,7 @@ class CompositeDict(SpecialDict):
         # Note - this returns the first child of the given name
         # any other children with similar names down the tree
         # is not considered.
-        
+
         for child in self.getAllChildren():
             if child.getName() == name:
                 return child
@@ -195,33 +200,33 @@ class CompositeDict(SpecialDict):
 
         # Note: this returns a list of all the children of a given
         # name, irrespective of the depth of look-up.
-        
+
         children = []
-        
+
         for child in self.getAllChildren():
             if child.getName() == name:
                 children.append(child)
 
         return children
-            
+
     def getPropertyDict(self):
         """ Return the property dictionary """
-        
+
         d = self.getChild('__properties')
         if d:
             return d.getDict()
         else:
             return {}
-        
+
     def getParent(self):
         """ Return the person who created me """
 
         return self._father
-    
+
     def __setChildDict(self, child):
         """ Private method to set the dictionary of the child
         object 'child' in the internal dictionary """
-        
+
         d = self[self._name]
         d[child.getName()] = child.getDict()
 
@@ -236,25 +241,25 @@ class CompositeDict(SpecialDict):
         # child is orphaned - see addChild and addChild2
         # methods !
         self._father = father
-        
+
     def setName(self, name):
-        """ Set the name of this ConfigInfo object to 'name' """        
+        """ Set the name of this ConfigInfo object to 'name' """
 
         self._name = name
 
     def setDict(self, d):
         """ Set the contained dictionary """
-        
+
         self[self._name] = d.copy()
-        
+
     def setAttribute(self, name, value):
         """ Set a name value pair in the contained dictionary """
-        
+
         self[self._name][name] = value
 
     def getAttribute(self, name):
         """ Return value of an attribute from the contained dictionary """
-        
+
         return self[self._name][name]
 
     def addChild(self, name, force=False):
@@ -264,13 +269,13 @@ class CompositeDict(SpecialDict):
 
         This function returns the child object, whether
         new or existing """
-        
+
         if type(name) != str:
             raise ValueError('Argument should be a string!')
-        
+
         child = self.getChild(name)
         if child:
-            # print 'Child %s present!' % name
+            # print('Child %s present!' % name)
             # Replace it if force==True
             if force:
                 index = self.getIndex(child)
@@ -278,22 +283,22 @@ class CompositeDict(SpecialDict):
                     child = self.__class__(name)
                     self._children[index] = child
                     child.setParent(self)
-                    
+
                     self.__setChildDict(child)
             return child
         else:
             child = self.__class__(name)
             child.setParent(self)
-            
+
             self._children.append(child)
             self.__setChildDict(child)
 
             return child
-        
+
     def addChild2(self, child):
         """ Add the child object 'child'. If it is already present,
         it is overwritten by default """
-        
+
         currChild = self.getChild(child.getName())
         if currChild:
             index = self.getIndex(currChild)
@@ -303,10 +308,10 @@ class CompositeDict(SpecialDict):
                 # Unset the existing child's parent
                 currChild.setParent(None)
                 del currChild
-                
+
                 self.__setChildDict(child)
         else:
-            child.setParent(self)            
+            child.setParent(self)
             self._children.append(child)
             self.__setChildDict(child)
 
@@ -316,7 +321,7 @@ if __name__ == "__main__":
     frame = window.addChild('Frame')
     tfield = frame.addChild('Text Field')
     tfield.setAttribute('size', '20')
-    
+
     btn = frame.addChild('Button1')
     btn.setAttribute('label', 'Submit')
 
@@ -329,3 +334,7 @@ if __name__ == "__main__":
     # print(window.Frame.Button2)
     print(window.Frame.Button1.label)
     print(window.Frame.Button2.label)
+
+### OUTPUT ###
+# Submit
+# Browse

+ 22 - 35
decorator.py

@@ -1,44 +1,31 @@
-# http://stackoverflow.com/questions/3118929/implementing-the-decorator-pattern-in-python
+"""https://docs.python.org/2/library/functools.html#functools.wraps"""
+"""https://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/739665#739665"""
 
+from functools import wraps
 
-class foo_decorator(object):
-    def __init__(self, decoratee):
-        self._decoratee = decoratee
 
-    def f1(self):
-        print("decorated f1")
-        self._decoratee.f1()
+def makebold(fn):
+    @wraps(fn)
+    def wrapped():
+        return "<b>" + fn() + "</b>"
+    return wrapped
 
-    def __getattr__(self, name):
-        return getattr(self._decoratee, name)
 
+def makeitalic(fn):
+    @wraps(fn)
+    def wrapped():
+        return "<i>" + fn() + "</i>"
+    return wrapped
 
-class undecorated_foo(object):
-    def f1(self):
-        print("original f1")
 
-    def f2(self):
-        print("original f2")
-
-
-@foo_decorator
-class decorated_foo(object):
-    def f1(self):
-        print("original f1")
-
-    def f2(self):
-        print("original f2")
-
-
-def main():
-    u = undecorated_foo()
-    v = foo_decorator(u)
-    # The @foo_decorator syntax is just shorthand for calling
-    # foo_decorator on the decorated object right after its
-    # declaration.
-
-    v.f1()
-    v.f2()
+@makebold
+@makeitalic
+def hello():
+    """a decorated hello world"""
+    return "hello world"
 
 if __name__ == '__main__':
-    main()
+    print('result:{}   name:{}   doc:{}'.format(hello(), hello.__name__, hello.__doc__))
+
+### OUTPUT ###
+# result:<b><i>hello world</i></b>   name:hello   doc:a decorated hello world

+ 26 - 1
facade.py

@@ -1,4 +1,5 @@
-"""http://dpip.testingperspective.com/?p=26"""
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
 
 import time
 
@@ -7,6 +8,7 @@ SLEEP = 0.5
 
 # Complex Parts
 class TC1:
+
     def run(self):
         print("###### In Test 1 ######")
         time.sleep(SLEEP)
@@ -20,6 +22,7 @@ class TC1:
 
 
 class TC2:
+
     def run(self):
         print("###### In Test 2 ######")
         time.sleep(SLEEP)
@@ -33,6 +36,7 @@ class TC2:
 
 
 class TC3:
+
     def run(self):
         print("###### In Test 3 ######")
         time.sleep(SLEEP)
@@ -47,6 +51,7 @@ class TC3:
 
 # Facade
 class TestRunner:
+
     def __init__(self):
         self.tc1 = TC1()
         self.tc2 = TC2()
@@ -61,3 +66,23 @@ class TestRunner:
 if __name__ == '__main__':
     testrunner = TestRunner()
     testrunner.runAll()
+
+### OUTPUT ###
+# ###### In Test 1 ######
+# Setting up
+# Running test
+# Tearing down
+# Test Finished
+#
+# ###### In Test 2 ######
+# Setting up
+# Running test
+# Tearing down
+# Test Finished
+#
+# ###### In Test 3 ######
+# Setting up
+# Running test
+# Tearing down
+# Test Finished
+#

+ 15 - 3
factory_method.py

@@ -1,14 +1,18 @@
-#encoding=utf-8
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/"""
 
 
 class GreekGetter:
+
     """A simple localizer a la gettext"""
+
     def __init__(self):
         self.trans = dict(dog="σκύλος", cat="γάτα")
 
     def get(self, msgid):
-        """We'll punt if we don't have a translation"""          
+        """We'll punt if we don't have a translation"""
         try:
             return self.trans[msgid]
         except KeyError:
@@ -16,7 +20,9 @@ class GreekGetter:
 
 
 class EnglishGetter:
-    """Simply echoes the msg ids"""     
+
+    """Simply echoes the msg ids"""
+
     def get(self, msgid):
         return str(msgid)
 
@@ -31,3 +37,9 @@ e, g = get_localizer("English"), get_localizer("Greek")
 # Localize some text
 for msgid in "dog parrot cat bear".split():
     print(e.get(msgid), g.get(msgid))
+
+### OUTPUT ###
+# dog σκύλος
+# parrot parrot
+# cat γάτα
+# bear bear

+ 9 - 0
flyweight.py

@@ -1,9 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://codesnipers.com/?q=python-flyweights"""
 
 import weakref
 
 
 class Card(object):
+
     """The object pool. Has builtin reference counting"""
     _CardPool = weakref.WeakValueDictionary()
 
@@ -31,3 +35,8 @@ if __name__ == '__main__':
     print(c1, c2)
     print(c1 == c2)
     print(id(c1), id(c2))
+
+### OUTPUT ###
+# <Card: 9h> <Card: 9h>
+# True
+# 140368617673296 140368617673296

+ 0 - 1
foo.txt

@@ -1 +0,0 @@
-All krakens crush undead, evil sails.

+ 37 - 27
graph_search.py

@@ -1,19 +1,24 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
 class GraphSearch:
+
     """Graph search emulation in python, from source
     http://www.python.org/doc/essays/graphs/"""
 
     def __init__(self, graph):
-        self.graph = graph 
-    
-    def find_path(self, start, end, path=[]):
+        self.graph = graph
+
+    def find_path(self, start, end, path=None):
         self.start = start
         self.end = end
-        self.path = path
+        self.path = path if path else []
 
         self.path += [self.start]
         if self.start == self.end:
             return self.path
-        if not self.graph.has_key(self.start):
+        if self.start not in self.graph:
             return None
         for node in self.graph[self.start]:
             if node not in self.path:
@@ -22,55 +27,60 @@ class GraphSearch:
                     return newpath
         return None
 
-    def find_all_path(self, start, end, path=[]):            
+    def find_all_path(self, start, end, path=None):
         self.start = start
         self.end = end
-        self.path = path
-        self.path += [self.start]
+        _path = path if path else []
+        _path += [self.start]
         if self.start == self.end:
-            return [self.path]
-        if not self.graph.has_key(self.start):
+            return [_path]
+        if self.start not in self.graph:
             return []
         paths = []
         for node in self.graph[self.start]:
-            if node not in self.path:
-                newpaths = self.find_all_path(node, self.end, self.path)
+            if node not in _path:
+                newpaths = self.find_all_path(node, self.end, _path[:])
                 for newpath in newpaths:
-                    paths.append(newpath)                
+                    paths.append(newpath)
         return paths
 
-    def find_shortest_path(self, start, end, path=[]):         
+    def find_shortest_path(self, start, end, path=None):
         self.start = start
         self.end = end
-        self.path = path
-        
-        self.path += [self.start]
+        _path = path if path else []
+
+        _path += [self.start]
         if self.start == self.end:
-            return self.path
-        if not self.graph.has_key(self.start):
+            return _path
+        if self.start not in self.graph:
             return None
         shortest = None
         for node in self.graph[self.start]:
-            if node not in self.path:
-                newpath = self.find_shortest_path(node, self.end, self.path)
+            if node not in _path:
+                newpath = self.find_shortest_path(node, self.end, _path[:])
                 if newpath:
                     if not shortest or len(newpath) < len(shortest):
                         shortest = newpath
         return shortest
 
-#example of graph usage
+# example of graph usage
 graph = {'A': ['B', 'C'],
          'B': ['C', 'D'],
          'C': ['D'],
          'D': ['C'],
          'E': ['F'],
-         'F': ['C']   
+         'F': ['C']
          }
 
-#inistialization of new graph search object
+# initialization of new graph search object
 graph1 = GraphSearch(graph)
 
 
-print graph1.find_path('A', 'D')
-print graph1.find_all_path('A', 'D')
-print graph1.find_shortest_path('A', 'D')
+print(graph1.find_path('A', 'D'))
+print(graph1.find_all_path('A', 'D'))
+print(graph1.find_shortest_path('A', 'D'))
+
+### OUTPUT ###
+# ['A', 'B', 'C', 'D']
+# [['A', 'B', 'C', 'D'], ['A', 'B', 'D'], ['A', 'C', 'D']]
+# ['A', 'B', 'D']

+ 9 - 0
iterator.py

@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
 
 Implementation of the iterator pattern with a generator"""
@@ -26,3 +29,9 @@ for number in count_to_five():
     print(number, end=' ')
 
 print()
+
+### OUTPUT ###
+# Counting to two...
+# one two
+# Counting to five...
+# one two three four five

+ 35 - 10
mediator.py

@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://dpip.testingperspective.com/?p=28"""
 
 import random
@@ -5,26 +8,27 @@ import time
 
 
 class TC:
+
     def __init__(self):
         self._tm = None
         self._bProblem = 0
 
     def setup(self):
         print("Setting up the Test")
-        time.sleep(1)
+        time.sleep(0.1)
         self._tm.prepareReporting()
 
     def execute(self):
         if not self._bProblem:
             print("Executing the test")
-            time.sleep(1)
+            time.sleep(0.1)
         else:
             print("Problem in setup. Test not executed.")
 
     def tearDown(self):
         if not self._bProblem:
             print("Tearing down")
-            time.sleep(1)
+            time.sleep(0.1)
             self._tm.publishReport()
         else:
             print("Test not executed. No tear down required.")
@@ -37,42 +41,44 @@ class TC:
 
 
 class Reporter:
+
     def __init__(self):
         self._tm = None
 
     def prepare(self):
         print("Reporter Class is preparing to report the results")
-        time.sleep(1)
+        time.sleep(0.1)
 
     def report(self):
         print("Reporting the results of Test")
-        time.sleep(1)
+        time.sleep(0.1)
 
     def setTM(self, tm):
         self._tm = tm
 
 
 class DB:
+
     def __init__(self):
         self._tm = None
 
     def insert(self):
         print("Inserting the execution begin status in the Database")
-        time.sleep(1)
-        #Following code is to simulate a communication from DB to TC
-        import random
+        time.sleep(0.1)
+        # Following code is to simulate a communication from DB to TC
         if random.randrange(1, 4) == 3:
             return -1
 
     def update(self):
         print("Updating the test results in Database")
-        time.sleep(1)
+        time.sleep(0.1)
 
     def setTM(self, tm):
         self._tm = tm
 
 
 class TestManager:
+
     def __init__(self):
         self._reporter = None
         self._db = None
@@ -109,10 +115,29 @@ if __name__ == '__main__':
     # For simplification we are looping on the same test.
     # Practically, it could be about various unique test classes and their
     # objects
-    while True:
+    for i in range(3):
         tc = TC()
         tc.setTM(tm)
         tm.setTC(tc)
         tc.setup()
         tc.execute()
         tc.tearDown()
+
+### OUTPUT ###
+# Setting up the Test
+# Inserting the execution begin status in the Database
+# Executing the test
+# Tearing down
+# Updating the test results in Database
+# Reporting the results of Test
+# Setting up the Test
+# Inserting the execution begin status in the Database
+# Reporter Class is preparing to report the results
+# Problem in setup. Test not executed.
+# Test not executed. No tear down required.
+# Setting up the Test
+# Inserting the execution begin status in the Database
+# Executing the test
+# Tearing down
+# Updating the test results in Database
+# Reporting the results of Test

+ 35 - 2
memento.py

@@ -1,4 +1,7 @@
-"""code.activestate.com/recipes/413838-memento-closure/"""
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""http://code.activestate.com/recipes/413838-memento-closure/"""
 
 import copy
 
@@ -13,6 +16,7 @@ def Memento(obj, deep=False):
 
 
 class Transaction:
+
     """A transaction guard. This is really just
       syntactic suggar arount a memento closure.
       """
@@ -31,9 +35,11 @@ class Transaction:
 
 
 class transactional(object):
+
     """Adds transactional semantics to methods. Methods decorated  with
     @transactional will rollback to entry state upon exceptions.
     """
+
     def __init__(self, method):
         self.method = method
 
@@ -49,6 +55,7 @@ class transactional(object):
 
 
 class NumObj(object):
+
     def __init__(self, value):
         self.value = value
 
@@ -88,7 +95,33 @@ if __name__ == '__main__':
         n.DoStuff()
     except:
         print('-> doing stuff failed!')
+        import sys
         import traceback
-        traceback.print_exc(0)
+        traceback.print_exc(file=sys.stdout)
         pass
     print(n)
+
+### OUTPUT ###
+# <NumObj: -1>
+# <NumObj: 0>
+# <NumObj: 1>
+# <NumObj: 2>
+# -- commited
+# <NumObj: 3>
+# <NumObj: 4>
+# <NumObj: 5>
+# -- rolled back
+# <NumObj: 2>
+# -- now doing stuff ...
+# -> doing stuff failed!
+# Traceback (most recent call last):
+#   File "memento.py", line 91, in <module>
+#     n.DoStuff()
+#   File "memento.py", line 47, in transaction
+#     return self.method(obj, *args, **kwargs)
+#   File "memento.py", line 67, in DoStuff
+# self.Increment()     # <- will fail and rollback
+#   File "memento.py", line 62, in Increment
+#     self.value += 1
+# TypeError: Can't convert 'int' object to str implicitly
+# <NumObj: 2>

+ 17 - 0
mvc.py

@@ -55,3 +55,20 @@ if __name__ == '__main__':
     controller.get_product_information('eggs')
     controller.get_product_information('milk')
     controller.get_product_information('arepas')
+
+### OUTPUT ###
+# PRODUCT LIST:
+# cheese
+# eggs
+# milk
+#
+# PRODUCT INFORMATION:
+# Name: Cheese, Price: 2.00, Quantity: 10
+#
+# PRODUCT INFORMATION:
+# Name: Eggs, Price: 0.20, Quantity: 100
+#
+# PRODUCT INFORMATION:
+# Name: Milk, Price: 1.50, Quantity: 10
+#
+# That product "arepas" does not exist in the records

+ 0 - 81
null.py

@@ -1,81 +0,0 @@
-#!/user/bin/env python 
-
-"""http://code.activestate.com/recipes/68205-null-object-design-pattern/"""
-
-
-class Null:
-    def __init__(self, *args, **kwargs):
-        """Ignore parameters."""
-        return None
-
-    def __call__(self, *args, **kwargs):
-        """Ignore method calls."""
-        return self
-
-    def __getattr__(self, mname):
-        """Ignore attribute requests."""
-        return self
-
-    def __setattr__(self, name, value):
-        """Ignore attribute setting."""
-        return self
-
-    def __delattr__(self, name):
-        """Ignore deleting attributes."""
-        return self
-
-    def __repr__(self):
-        """Return a string representation."""
-        return "<Null>"
-
-    def __str__(self):
-        """Convert to a string and return it."""
-        return "Null"
-
-
-def test():
-    """Perform some decent tests, or rather: demos."""
-
-    # constructing and calling
-
-    n = Null()
-    print(n)
-
-    n = Null('value')
-    print(n)
-
-    n = Null('value', param='value')
-    print(n)
-
-    n()
-    n('value')
-    n('value', param='value')
-    print(n)
-
-    # attribute handling
-
-    n.attr1
-    print('attr1', n.attr1)
-    n.attr1.attr2
-    n.method1()
-    n.method1().method2()
-    n.method('value')
-    n.method(param='value')
-    n.method('value', param='value')
-    n.attr1.method1()
-    n.method1().attr1
-
-    n.attr1 = 'value'
-    n.attr1.attr2 = 'value'
-
-    del n.attr1
-    del n.attr1.attr2.attr3
-
-    # representation and conversion to a string
-    
-    assert repr(n) == '<Null>'
-    assert str(n) == 'Null'
-
-
-if __name__ == '__main__':
-    test()

+ 28 - 2
observer.py

@@ -1,12 +1,16 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://code.activestate.com/recipes/131499-observer-pattern/"""
 
 
 class Subject(object):
+
     def __init__(self):
         self._observers = []
 
     def attach(self, observer):
-        if not observer in self._observers:
+        if observer not in self._observers:
             self._observers.append(observer)
 
     def detach(self, observer):
@@ -23,6 +27,7 @@ class Subject(object):
 
 # Example usage
 class Data(Subject):
+
     def __init__(self, name=''):
         Subject.__init__(self)
         self.name = name
@@ -31,7 +36,7 @@ class Data(Subject):
     @property
     def data(self):
         return self._data
-    
+
     @data.setter
     def data(self, value):
         self._data = value
@@ -39,12 +44,14 @@ class Data(Subject):
 
 
 class HexViewer:
+
     def update(self, subject):
         print('HexViewer: Subject %s has data 0x%x' %
               (subject.name, subject.data))
 
 
 class DecimalViewer:
+
     def update(self, subject):
         print('DecimalViewer: Subject %s has data %d' %
               (subject.name, subject.data))
@@ -80,3 +87,22 @@ def main():
 
 if __name__ == '__main__':
     main()
+
+### OUTPUT ###
+# Setting Data 1 = 10
+# DecimalViewer: Subject Data 1 has data 10
+# HexViewer: Subject Data 1 has data 0xa
+# Setting Data 2 = 15
+# HexViewer: Subject Data 2 has data 0xf
+# DecimalViewer: Subject Data 2 has data 15
+# Setting Data 1 = 3
+# DecimalViewer: Subject Data 1 has data 3
+# HexViewer: Subject Data 1 has data 0x3
+# Setting Data 2 = 5
+# HexViewer: Subject Data 2 has data 0x5
+# DecimalViewer: Subject Data 2 has data 5
+# Detach HexViewer from data1 and data2.
+# Setting Data 1 = 10
+# DecimalViewer: Subject Data 1 has data 10
+# Setting Data 2 = 15
+# DecimalViewer: Subject Data 2 has data 15

+ 10 - 0
pool.py

@@ -1,7 +1,11 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 """http://stackoverflow.com/questions/1514120/python-implementation-of-the-object-pool-design-pattern"""
 
 
 class QueueObject():
+
     def __init__(self, queue, auto_get=False):
         self._queue = queue
         self.object = self._queue.get() if auto_get else None
@@ -49,3 +53,9 @@ def main():
 
 if __name__ == '__main__':
     main()
+
+### OUTPUT ###
+# Inside with: yam
+# Outside with: yam
+# Inside func: sam
+# Outside func: sam

+ 22 - 9
prototype.py

@@ -1,7 +1,11 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
 import copy
 
 
 class Prototype:
+
     def __init__(self):
         self._objects = {}
 
@@ -20,18 +24,27 @@ class Prototype:
         return obj
 
 
-def main():
-    class A:
-        pass
+class A:
+    def __init__(self):
+        self.x = 3
+        self.y = 8
+        self.z = 15
+        self.garbage = [38, 11, 19]
 
-    a = A()
-    prototype = Prototype()
-    prototype.register_object('a', a)
-    b = prototype.clone('a', a=1, b=2, c=3)
+    def __str__(self):
+        return '{} {} {} {}'.format(self.x, self.y, self.z, self.garbage)
 
-    print(a)
-    print(b.a, b.b, b.c)
 
+def main():
+    a = A()
+    prototype = Prototype()
+    prototype.register_object('objecta', a)
+    b = prototype.clone('objecta')
+    c = prototype.clone('objecta', x=1, y=2, garbage=[88, 1])
+    print([str(i) for i in (a, b, c)])
 
 if __name__ == '__main__':
     main()
+
+### OUTPUT ###
+# ['3 8 15 [38, 11, 19]', '3 8 15 [38, 11, 19]', '1 2 15 [88, 1]']

+ 24 - 13
proxy.py

@@ -1,26 +1,31 @@
-import time 
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import time
 
 
 class SalesManager:
-    def work(self):                         
-        print("Sales Manager working...")             
 
-    def talk(self):                         
-        print("Sales Manager ready to talk") 
+    def work(self):
+        print("Sales Manager working...")
+
+    def talk(self):
+        print("Sales Manager ready to talk")
 
 
 class Proxy:
-    def __init__(self):                         
-        self.busy = 'No'                         
-        self.sales = None             
+
+    def __init__(self):
+        self.busy = 'No'
+        self.sales = None
 
     def work(self):
-        print("Proxy checking for Sales Manager availability")                         
-        if self.busy == 'No':                                      
-            self.sales = SalesManager()                                      
+        print("Proxy checking for Sales Manager availability")
+        if self.busy == 'No':
+            self.sales = SalesManager()
             time.sleep(2)
-            self.sales.talk()                         
-        else:                                      
+            self.sales.talk()
+        else:
             time.sleep(2)
             print("Sales Manager is busy")
 
@@ -30,3 +35,9 @@ if __name__ == '__main__':
     p.work()
     p.busy = 'Yes'
     p.work()
+
+### OUTPUT ###
+# Proxy checking for Sales Manager availability
+# Sales Manager ready to talk
+# Proxy checking for Sales Manager availability
+# Sales Manager is busy

+ 92 - 0
publish_subscribe.py

@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Reference: http://www.slideshare.net/ishraqabd/publish-subscribe-model-overview-13368808
+Author: https://github.com/HanWenfang
+"""
+
+
+class Provider:
+
+    def __init__(self):
+        self.msg_queue = []
+        self.subscribers = {}
+
+    def notify(self, msg):
+        self.msg_queue.append(msg)
+
+    def subscribe(self, msg, subscriber):
+        if msg not in self.subscribers:
+            self.subscribers[msg] = []
+            self.subscribers[msg].append(subscriber)  # unfair
+        else:
+            self.subscribers[msg].append(subscriber)
+
+    def unsubscribe(self, msg, subscriber):
+        self.subscribers[msg].remove(subscriber)
+
+    def update(self):
+        for msg in self.msg_queue:
+            if msg in self.subscribers:
+                for sub in self.subscribers[msg]:
+                    sub.run(msg)
+        self.msg_queue = []
+
+
+class Publisher:
+
+    def __init__(self, msg_center):
+        self.provider = msg_center
+
+    def publish(self, msg):
+        self.provider.notify(msg)
+
+
+class Subscriber:
+
+    def __init__(self, name, msg_center):
+        self.name = name
+        self.provider = msg_center
+
+    def subscribe(self, msg):
+        self.provider.subscribe(msg, self)
+
+    def run(self, msg):
+        print("{} got {}".format(self.name, msg))
+
+
+def main():
+    message_center = Provider()
+
+    fftv = Publisher(message_center)
+
+    jim = Subscriber("jim", message_center)
+    jim.subscribe("cartoon")
+    jack = Subscriber("jack", message_center)
+    jack.subscribe("music")
+    gee = Subscriber("gee", message_center)
+    gee.subscribe("movie")
+
+    fftv.publish("cartoon")
+    fftv.publish("music")
+    fftv.publish("ads")
+    fftv.publish("movie")
+    fftv.publish("cartoon")
+    fftv.publish("cartoon")
+    fftv.publish("movie")
+    fftv.publish("blank")
+
+    message_center.update()
+
+
+if __name__ == "__main__":
+    main()
+
+### OUTPUT ###
+# jim got cartoon
+# jack got music
+# gee got movie
+# jim got cartoon
+# jim got cartoon
+# gee got movie

+ 17 - 0
state.py

@@ -4,6 +4,7 @@
 
 
 class State(object):
+
     """Base state. This is to share functionality"""
 
     def scan(self):
@@ -15,6 +16,7 @@ class State(object):
 
 
 class AmState(State):
+
     def __init__(self, radio):
         self.radio = radio
         self.stations = ["1250", "1380", "1510"]
@@ -27,6 +29,7 @@ class AmState(State):
 
 
 class FmState(State):
+
     def __init__(self, radio):
         self.radio = radio
         self.stations = ["81.3", "89.1", "103.9"]
@@ -39,7 +42,9 @@ class FmState(State):
 
 
 class Radio(object):
+
     """A radio.     It has a scan button, and an AM/FM toggle switch."""
+
     def __init__(self):
         """We have an AM state and an FM state"""
         self.amstate = AmState(self)
@@ -61,3 +66,15 @@ if __name__ == '__main__':
 
     for action in actions:
         action()
+
+### OUTPUT ###
+# Scanning... Station is 1380 AM
+# Scanning... Station is 1510 AM
+# Switching to FM
+# Scanning... Station is 89.1 FM
+# Scanning... Station is 103.9 FM
+# Scanning... Station is 81.3 FM
+# Scanning... Station is 89.1 FM
+# Switching to AM
+# Scanning... Station is 1250 AM
+# Scanning... Station is 1380 AM

+ 6 - 0
strategy.py

@@ -12,6 +12,7 @@ import types
 
 
 class StrategyExample:
+
     def __init__(self, func=None):
         self.name = 'Strategy Example 0'
         if func is not None:
@@ -41,3 +42,8 @@ if __name__ == '__main__':
     strat0.execute()
     strat1.execute()
     strat2.execute()
+
+### OUTPUT ###
+# Strategy Example 0
+# Strategy Example 1 from execute 1
+# Strategy Example 2 from execute 2

+ 50 - 0
template.py

@@ -55,3 +55,53 @@ templates = [make_template(s, g, a)
 # Execute them
 for template in templates:
     template()
+
+### OUTPUT ###
+# spam
+# ----------
+# eggs
+# ----------
+# apple
+# ----------
+# apple
+# ----------
+# eggs
+# ----------
+# spam
+# ----------
+# maps
+# ----------
+# sgge
+# ----------
+# elppa
+# ----------
+# elppa
+# ----------
+# sgge
+# ----------
+# maps
+# ----------
+# ['s', 'p', 'a', 'm']
+# ----------
+# ['e', 'g', 'g', 's']
+# ----------
+# ['a', 'p', 'p', 'l', 'e']
+# ----------
+# ['a', 'p', 'p', 'l', 'e']
+# ----------
+# ['e', 'g', 'g', 's']
+# ----------
+# ['s', 'p', 'a', 'm']
+# ----------
+# ['m', 'a', 'p', 's']
+# ----------
+# ['s', 'g', 'g', 'e']
+# ----------
+# ['e', 'l', 'p', 'p', 'a']
+# ----------
+# ['e', 'l', 'p', 'p', 'a']
+# ----------
+# ['s', 'g', 'g', 'e']
+# ----------
+# ['m', 'a', 'p', 's']
+# ----------

+ 9 - 3
visitor.py

@@ -18,10 +18,11 @@ class C(A, B):
 
 
 class Visitor(object):
+
     def visit(self, node, *args, **kwargs):
         meth = None
         for cls in node.__class__.__mro__:
-            meth_name = 'visit_'+cls.__name__
+            meth_name = 'visit_' + cls.__name__
             meth = getattr(self, meth_name, None)
             if meth:
                 break
@@ -31,10 +32,10 @@ class Visitor(object):
         return meth(node, *args, **kwargs)
 
     def generic_visit(self, node, *args, **kwargs):
-        print('generic_visit '+node.__class__.__name__)
+        print('generic_visit ' + node.__class__.__name__)
 
     def visit_B(self, node, *args, **kwargs):
-        print('visit_B '+node.__class__.__name__)
+        print('visit_B ' + node.__class__.__name__)
 
 
 a = A()
@@ -44,3 +45,8 @@ visitor = Visitor()
 visitor.visit(a)
 visitor.visit(b)
 visitor.visit(c)
+
+### OUTPUT ###
+# generic_visit A
+# visit_B B
+# visit_B C