├── .gitignore ├── 3-tier.py ├── README.md ├── abstract_factory.py ├── adapter.py ├── append_output.sh ├── borg.py ├── bridge.py ├── builder.py ├── catalog.py ├── chain.py ├── chaining_method.py ├── command.py ├── composite.py ├── decorator.py ├── facade.py ├── factory_method.py ├── flyweight.py ├── front_controller.py ├── graph_search.py ├── iterator.py ├── lazy_evaluation.py ├── mediator.py ├── memento.py ├── mvc.py ├── observer.py ├── pool.py ├── prototype.py ├── proxy.py ├── publish_subscribe.py ├── registry.py ├── specification.py ├── state.py ├── strategy.py ├── template.py └── visitor.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /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 | """ Business logic holding data store instances """ 21 | 22 | data = Data() 23 | 24 | def product_list(self): 25 | return self.data['products'].keys() 26 | 27 | def product_information(self, product): 28 | return self.data['products'].get(product, None) 29 | 30 | 31 | class Ui(object): 32 | """ UI interaction class """ 33 | 34 | def __init__(self): 35 | self.business_logic = BusinessLogic() 36 | 37 | def get_product_list(self): 38 | print('PRODUCT LIST:') 39 | for product in self.business_logic.product_list(): 40 | print(product) 41 | print('') 42 | 43 | def get_product_information(self, product): 44 | product_info = self.business_logic.product_information(product) 45 | if product_info: 46 | print('PRODUCT INFORMATION:') 47 | print('Name: {0}, Price: {1:.2f}, Quantity: {2:}'.format( 48 | product.title(), product_info.get('price', 0), 49 | product_info.get('quantity', 0))) 50 | else: 51 | print('That product "{0}" does not exist in the records'.format( 52 | product)) 53 | 54 | 55 | def main(): 56 | ui = Ui() 57 | ui.get_product_list() 58 | ui.get_product_information('cheese') 59 | ui.get_product_information('eggs') 60 | ui.get_product_information('milk') 61 | ui.get_product_information('arepas') 62 | 63 | if __name__ == '__main__': 64 | main() 65 | 66 | ### OUTPUT ### 67 | # PRODUCT LIST: 68 | # (Fetching from Data Store) 69 | # cheese 70 | # eggs 71 | # milk 72 | # 73 | # (Fetching from Data Store) 74 | # PRODUCT INFORMATION: 75 | # Name: Cheese, Price: 2.00, Quantity: 10 76 | # (Fetching from Data Store) 77 | # PRODUCT INFORMATION: 78 | # Name: Eggs, Price: 0.20, Quantity: 100 79 | # (Fetching from Data Store) 80 | # PRODUCT INFORMATION: 81 | # Name: Milk, Price: 1.50, Quantity: 10 82 | # (Fetching from Data Store) 83 | # That product "arepas" does not exist in the records 84 | -------------------------------------------------------------------------------- /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 | | [front_controller](front_controller.py) | single handler requests coming to the application | 29 | | [flyweight](flyweight.py) | transparently reuse existing instances of objects with similar/identical state | 30 | | [graph_search](graph_search.py) | (graphing algorithms, not design patterns) | 31 | | [lazy_evaluation](lazy_evaluation.py) | lazily-evaluated property pattern in Python | 32 | | [mediator](mediator.py) | an object that knows how to connect other objects and act as a proxy | 33 | | [memento](memento.py) | generate an opaque token that can be used to go back to a previous state | 34 | | [mvc](mvc.py) | model<->view<->controller (non-strict relationships) | 35 | | [observer](observer.py) | provide a callback for notification of events/changes to data | 36 | | [pool](pool.py) | preinstantiate and maintain a group of instances of the same type | 37 | | [prototype](prototype.py) | use a factory and clones of a prototype for new instances (if instantiation is expensive) | 38 | | [proxy](proxy.py) | an object funnels operations to something else | 39 | | [publish_subscribe](publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners | 40 | | [registry](registry.py) | keeping track of all subclasses of a given class | 41 | | [specification](specification.py) | business rules can be recombined by chaining the business rules together using boolean logic | 42 | | [state](state.py) | logic is org'd into a discrete number of potential states and the next state that can be transitioned to | 43 | | [strategy](strategy.py) | selectable operations over the same data | 44 | | [template](template.py) | an object imposes a structure but takes pluggable components | 45 | | [visitor](visitor.py) | invoke a callback for all items of a collection | 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | Adapts an object by replacing methods. 42 | Usage: 43 | dog = Dog 44 | dog = Adapter(dog, dict(make_noise=dog.bark)) 45 | 46 | >>> objects = [] 47 | >>> dog = Dog() 48 | >>> objects.append(Adapter(dog, make_noise=dog.bark)) 49 | >>> cat = Cat() 50 | >>> objects.append(Adapter(cat, make_noise=cat.meow)) 51 | >>> human = Human() 52 | >>> objects.append(Adapter(human, make_noise=human.speak)) 53 | >>> car = Car() 54 | >>> car_noise = lambda: car.make_noise(3) 55 | >>> objects.append(Adapter(car, make_noise=car_noise)) 56 | 57 | >>> for obj in objects: 58 | ... print('A {} goes {}'.format(obj.name, obj.make_noise())) 59 | A Dog goes woof! 60 | A Cat goes meow! 61 | A Human goes 'hello' 62 | A Car goes vroom!!! 63 | """ 64 | 65 | def __init__(self, obj, **adapted_methods): 66 | """We set the adapted methods in the object's dict""" 67 | self.obj = obj 68 | self.__dict__.update(adapted_methods) 69 | 70 | def __getattr__(self, attr): 71 | """All non-adapted calls are passed to the object""" 72 | return getattr(self.obj, attr) 73 | 74 | 75 | def main(): 76 | objects = [] 77 | dog = Dog() 78 | objects.append(Adapter(dog, make_noise=dog.bark)) 79 | cat = Cat() 80 | objects.append(Adapter(cat, make_noise=cat.meow)) 81 | human = Human() 82 | objects.append(Adapter(human, make_noise=human.speak)) 83 | car = Car() 84 | objects.append(Adapter(car, make_noise=lambda: car.make_noise(3))) 85 | 86 | for obj in objects: 87 | print("A {0} goes {1}".format(obj.name, obj.make_noise())) 88 | 89 | 90 | if __name__ == "__main__": 91 | main() 92 | 93 | ### OUTPUT ### 94 | # A Dog goes woof! 95 | # A Cat goes meow! 96 | # A Human goes 'hello' 97 | # A Car goes vroom!!! 98 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /builder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding : utf-8 -*- 3 | 4 | """ 5 | @author: Diogenes Augusto Fernandes Herminio 6 | https://gist.github.com/420905#file_builder_python.py 7 | """ 8 | 9 | 10 | # Director 11 | class Director(object): 12 | 13 | def __init__(self): 14 | self.builder = None 15 | 16 | def construct_building(self): 17 | self.builder.new_building() 18 | self.builder.build_floor() 19 | self.builder.build_size() 20 | 21 | def get_building(self): 22 | return self.builder.building 23 | 24 | 25 | # Abstract Builder 26 | class Builder(object): 27 | 28 | def __init__(self): 29 | self.building = None 30 | 31 | def new_building(self): 32 | self.building = Building() 33 | 34 | 35 | # Concrete Builder 36 | class BuilderHouse(Builder): 37 | 38 | def build_floor(self): 39 | self.building.floor = 'One' 40 | 41 | def build_size(self): 42 | self.building.size = 'Big' 43 | 44 | 45 | class BuilderFlat(Builder): 46 | 47 | def build_floor(self): 48 | self.building.floor = 'More than One' 49 | 50 | def build_size(self): 51 | self.building.size = 'Small' 52 | 53 | 54 | # Product 55 | class Building(object): 56 | 57 | def __init__(self): 58 | self.floor = None 59 | self.size = None 60 | 61 | def __repr__(self): 62 | return 'Floor: {0.floor} | Size: {0.size}'.format(self) 63 | 64 | 65 | # Client 66 | if __name__ == "__main__": 67 | director = Director() 68 | director.builder = BuilderHouse() 69 | director.construct_building() 70 | building = director.get_building() 71 | print(building) 72 | director.builder = BuilderFlat() 73 | director.construct_building() 74 | building = director.get_building() 75 | print(building) 76 | 77 | ### OUTPUT ### 78 | # Floor: One | Size: Big 79 | # Floor: More than One | Size: Small 80 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | """http://www.dabeaz.com/coroutines/""" 6 | 7 | import time 8 | import os 9 | import sys 10 | from contextlib import contextmanager 11 | 12 | class Handler: 13 | def __init__(self, successor=None): 14 | self._successor = successor 15 | def handle(self, request): 16 | res = self._handle(request) 17 | if not res: 18 | self._successor.handle(request) 19 | def _handle(self, request): 20 | raise NotImplementedError('Must provide implementation in subclass.') 21 | 22 | 23 | class ConcreteHandler1(Handler): 24 | 25 | def _handle(self, request): 26 | if 0 < request <= 10: 27 | print('request {} handled in handler 1'.format(request)) 28 | return True 29 | 30 | class ConcreteHandler2(Handler): 31 | 32 | def _handle(self, request): 33 | if 10 < request <= 20: 34 | print('request {} handled in handler 2'.format(request)) 35 | return True 36 | 37 | class ConcreteHandler3(Handler): 38 | 39 | def _handle(self, request): 40 | if 20 < request <= 30: 41 | print('request {} handled in handler 3'.format(request)) 42 | return True 43 | class DefaultHandler(Handler): 44 | 45 | def _handle(self, request): 46 | print('end of chain, no handler for {}'.format(request)) 47 | return True 48 | 49 | 50 | class Client: 51 | def __init__(self): 52 | self.handler = ConcreteHandler1(ConcreteHandler3(ConcreteHandler2(DefaultHandler()))) 53 | def delegate(self, requests): 54 | for request in requests: 55 | self.handler.handle(request) 56 | 57 | 58 | def coroutine(func): 59 | def start(*args, **kwargs): 60 | cr = func(*args, **kwargs) 61 | cr.next() 62 | return cr 63 | return start 64 | 65 | @coroutine 66 | def coroutine1(target): 67 | while True: 68 | request = yield 69 | if 0 < request <= 10: 70 | print('request {} handled in coroutine 1'.format(request)) 71 | else: 72 | target.send(request) 73 | 74 | @coroutine 75 | def coroutine2(target): 76 | while True: 77 | request = yield 78 | if 10 < request <= 20: 79 | print('request {} handled in coroutine 2'.format(request)) 80 | else: 81 | target.send(request) 82 | 83 | @coroutine 84 | def coroutine3(target): 85 | while True: 86 | request = yield 87 | if 20 < request <= 30: 88 | print('request {} handled in coroutine 3'.format(request)) 89 | else: 90 | target.send(request) 91 | 92 | @coroutine 93 | def default_coroutine(): 94 | while True: 95 | request = yield 96 | print('end of chain, no coroutine for {}'.format(request)) 97 | 98 | class ClientCoroutine: 99 | def __init__(self): 100 | self.target = coroutine1(coroutine3(coroutine2(default_coroutine()))) 101 | 102 | def delegate(self, requests): 103 | for request in requests: 104 | self.target.send(request) 105 | 106 | def timeit(func): 107 | 108 | def count(*args, **kwargs): 109 | start = time.time() 110 | res = func(*args, **kwargs) 111 | count._time = time.time() - start 112 | return res 113 | return count 114 | 115 | @contextmanager 116 | def suppress_stdout(): 117 | try: 118 | stdout, sys.stdout = sys.stdout, open(os.devnull, 'w') 119 | yield 120 | finally: 121 | sys.stdout = stdout 122 | 123 | 124 | if __name__ == "__main__": 125 | client1 = Client() 126 | client2 = ClientCoroutine() 127 | requests = [2, 5, 14, 22, 18, 3, 35, 27, 20] 128 | 129 | client1.delegate(requests) 130 | print('-'*30) 131 | client2.delegate(requests) 132 | 133 | requests *= 10000 134 | client1_delegate = timeit(client1.delegate) 135 | client2_delegate = timeit(client2.delegate) 136 | with suppress_stdout(): 137 | client1_delegate(requests) 138 | client2_delegate(requests) 139 | # lets check what is faster 140 | print(client1_delegate._time, client2_delegate._time) 141 | 142 | ### OUTPUT ### 143 | # request 2 handled in handler 1 144 | # request 5 handled in handler 1 145 | # request 14 handled in handler 2 146 | # request 22 handled in handler 3 147 | # request 18 handled in handler 2 148 | # request 3 handled in handler 1 149 | # end of chain, no handler for 35 150 | # request 27 handled in handler 3 151 | # request 20 handled in handler 2 152 | # ------------------------------ 153 | # request 2 handled in coroutine 1 154 | # request 5 handled in coroutine 1 155 | # request 14 handled in coroutine 2 156 | # request 22 handled in coroutine 3 157 | # request 18 handled in coroutine 2 158 | # request 3 handled in coroutine 1 159 | # end of chain, no coroutine for 35 160 | # request 27 handled in coroutine 3 161 | # request 20 handled in coroutine 2 162 | # (0.2369999885559082, 0.16199994087219238) 163 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 FlyweightMeta(type): 10 | def __new__(mcs, name, parents, dct): 11 | """ 12 | 13 | :param name: class name 14 | :param parents: class parents 15 | :param dct: dict: includes class attributes, class methods, 16 | static methods, etc 17 | :return: new class 18 | """ 19 | 20 | # set up instances pool 21 | dct['pool'] = weakref.WeakValueDictionary() 22 | return super(FlyweightMeta, mcs).__new__(mcs, name, parents, dct) 23 | 24 | @staticmethod 25 | def _serialize_params(cls, *args, **kwargs): 26 | """Serialize input parameters to a key. 27 | Simple implementation is just to serialize it as a string 28 | 29 | """ 30 | args_list = map(str, args) 31 | args_list.extend([str(kwargs), cls.__name__]) 32 | key = ''.join(args_list) 33 | return key 34 | 35 | def __call__(cls, *args, **kwargs): 36 | key = FlyweightMeta._serialize_params(cls, *args, **kwargs) 37 | pool = getattr(cls, 'pool', {}) 38 | 39 | instance = pool.get(key) 40 | if not instance: 41 | instance = super(FlyweightMeta, cls).__call__(*args, **kwargs) 42 | pool[key] = instance 43 | return instance 44 | 45 | 46 | class Card(object): 47 | 48 | """The object pool. Has builtin reference counting""" 49 | _CardPool = weakref.WeakValueDictionary() 50 | 51 | """Flyweight implementation. If the object exists in the 52 | pool just return it (instead of creating a new one)""" 53 | def __new__(cls, value, suit): 54 | obj = Card._CardPool.get(value + suit) 55 | if not obj: 56 | obj = object.__new__(cls) 57 | Card._CardPool[value + suit] = obj 58 | obj.value, obj.suit = value, suit 59 | return obj 60 | 61 | # def __init__(self, value, suit): 62 | # self.value, self.suit = value, suit 63 | 64 | def __repr__(self): 65 | return "" % (self.value, self.suit) 66 | 67 | 68 | class Card2(object): 69 | __metaclass__ = FlyweightMeta 70 | 71 | def __init__(self, *args, **kwargs): 72 | # print('Init {}: {}'.format(self.__class__, (args, kwargs))) 73 | pass 74 | 75 | 76 | if __name__ == '__main__': 77 | # comment __new__ and uncomment __init__ to see the difference 78 | c1 = Card('9', 'h') 79 | c2 = Card('9', 'h') 80 | print(c1, c2) 81 | print(c1 == c2, c1 is c2) 82 | print(id(c1), id(c2)) 83 | 84 | c1.temp = None 85 | c3 = Card('9', 'h') 86 | print(hasattr(c3, 'temp')) 87 | c1 = c2 = c3 = None 88 | c3 = Card('9', 'h') 89 | print(hasattr(c3, 'temp')) 90 | 91 | # Tests with metaclass 92 | instances_pool = getattr(Card2, 'pool') 93 | cm1 = Card2('10', 'h', a=1) 94 | cm2 = Card2('10', 'h', a=1) 95 | cm3 = Card2('10', 'h', a=2) 96 | 97 | assert (cm1 == cm2) != cm3 98 | assert (cm1 is cm2) is not cm3 99 | assert len(instances_pool) == 2 100 | 101 | del cm1 102 | assert len(instances_pool) == 2 103 | 104 | del cm2 105 | assert len(instances_pool) == 1 106 | 107 | del cm3 108 | assert len(instances_pool) == 0 109 | 110 | ### OUTPUT ### 111 | # (, ) 112 | # (True, True) 113 | # (31903856, 31903856) 114 | # True 115 | # False 116 | -------------------------------------------------------------------------------- /front_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @author: Gordeev Andrey 6 | The controller provides a centralized entry point that controls and manages 7 | request handling. 8 | """ 9 | 10 | 11 | class MobileView(object): 12 | def show_index_page(self): 13 | print('Displaying mobile index page') 14 | 15 | 16 | class TabletView(object): 17 | def show_index_page(self): 18 | print('Displaying tablet index page') 19 | 20 | 21 | class Dispatcher(object): 22 | def __init__(self): 23 | self.mobile_view = MobileView() 24 | self.tablet_view = TabletView() 25 | 26 | def dispatch(self, request): 27 | if request.type == Request.mobile_type: 28 | self.mobile_view.show_index_page() 29 | elif request.type == Request.tablet_type: 30 | self.tablet_view.show_index_page() 31 | else: 32 | print('cant dispatch the request') 33 | 34 | 35 | class RequestController(object): 36 | """ front controller """ 37 | def __init__(self): 38 | self.dispatcher = Dispatcher() 39 | 40 | def dispatch_request(self, request): 41 | if isinstance(request, Request): 42 | self.dispatcher.dispatch(request) 43 | else: 44 | print('request must be a Request object') 45 | 46 | 47 | class Request(object): 48 | """ request """ 49 | 50 | mobile_type = 'mobile' 51 | tablet_type = 'tablet' 52 | 53 | def __init__(self, request): 54 | self.type = None 55 | request = request.lower() 56 | if request == self.mobile_type: 57 | self.type = self.mobile_type 58 | elif request == self.tablet_type: 59 | self.type = self.tablet_type 60 | 61 | 62 | if __name__ == '__main__': 63 | front_controller = RequestController() 64 | front_controller.dispatch_request(Request('mobile')) 65 | front_controller.dispatch_request(Request('tablet')) 66 | 67 | front_controller.dispatch_request(Request('desktop')) 68 | front_controller.dispatch_request('mobile') 69 | 70 | 71 | ### OUTPUT ### 72 | # Displaying mobile index page 73 | # Displaying tablet index page 74 | # cant dispatch the request 75 | # request must be a Request object -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /memento.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """http://code.activestate.com/recipes/413838-memento-closure/""" 5 | 6 | from copy import copy, deepcopy 7 | 8 | 9 | def memento(obj, deep=False): 10 | state = copy(obj.__dict__) if deep else deepcopy(obj.__dict__) 11 | 12 | def restore(): 13 | obj.__dict__.clear() 14 | obj.__dict__.update(state) 15 | 16 | return restore 17 | 18 | 19 | class Transaction: 20 | """A transaction guard. 21 | This is, in fact, just syntactic sugar around a memento closure. 22 | """ 23 | deep = False 24 | states = [] 25 | 26 | def __init__(self, *targets): 27 | self.targets = targets 28 | self.commit() 29 | 30 | def commit(self): 31 | self.states = [memento(target, self.deep) for target in self.targets] 32 | 33 | def rollback(self): 34 | for a_state in self.states: 35 | a_state() 36 | 37 | 38 | class Transactional(object): 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 Exception as e: 52 | state() 53 | raise e 54 | 55 | return transaction 56 | 57 | 58 | class NumObj(object): 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 do_stuff(self): 70 | self.value = '1111' # <- invalid value 71 | self.increment() # <- will fail and rollback 72 | 73 | 74 | if __name__ == '__main__': 75 | num_obj = NumObj(-1) 76 | print(num_obj) 77 | 78 | a_transaction = Transaction(num_obj) 79 | try: 80 | for i in range(3): 81 | num_obj.increment() 82 | print(num_obj) 83 | a_transaction.commit() 84 | print('-- committed') 85 | 86 | for i in range(3): 87 | num_obj.increment() 88 | print(num_obj) 89 | num_obj.value += 'x' # will fail 90 | print(num_obj) 91 | except Exception as e: 92 | a_transaction.rollback() 93 | print('-- rolled back') 94 | print(num_obj) 95 | 96 | print('-- now doing stuff ...') 97 | try: 98 | num_obj.do_stuff() 99 | except Exception as e: 100 | print('-> doing stuff failed!') 101 | import sys 102 | import traceback 103 | 104 | traceback.print_exc(file=sys.stdout) 105 | print(num_obj) 106 | 107 | 108 | ### OUTPUT ### 109 | # 110 | # 111 | # 112 | # 113 | # -- committed 114 | # 115 | # 116 | # 117 | # -- rolled back 118 | # 119 | # -- now doing stuff ... 120 | # -> doing stuff failed! 121 | # Traceback (most recent call last): 122 | # File "memento.py", line 97, in 123 | # num_obj.do_stuff() 124 | # File "memento.py", line 52, in transaction 125 | # raise e 126 | # File "memento.py", line 49, in transaction 127 | # return self.method(obj, *args, **kwargs) 128 | # File "memento.py", line 70, in do_stuff 129 | # self.increment() # <- will fail and rollback 130 | # File "memento.py", line 65, in increment 131 | # self.value += 1 132 | # TypeError: Can't convert 'int' object to str implicitly 133 | # 134 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /registry.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class RegistryHolder(type): 6 | 7 | REGISTRY = {} 8 | 9 | def __new__(cls, name, bases, attrs): 10 | new_cls = type.__new__(cls, name, bases, attrs) 11 | """ 12 | Here the name of the class is used as key but it could be any class 13 | parameter. 14 | """ 15 | cls.REGISTRY[new_cls.__name__] = new_cls 16 | return new_cls 17 | 18 | @classmethod 19 | def get_registry(cls): 20 | return dict(cls.REGISTRY) 21 | 22 | 23 | class BaseRegisteredClass(metaclass=RegistryHolder): 24 | """ 25 | Any class that will inherits from BaseRegisteredClass will be included 26 | inside the dict RegistryHolder.REGISTRY, the key being the name of the 27 | class and the associated value, the class itself. 28 | """ 29 | pass 30 | 31 | if __name__ == "__main__": 32 | print("Before subclassing: ") 33 | for k in RegistryHolder.REGISTRY: 34 | print(k) 35 | 36 | class ClassRegistree(BaseRegisteredClass): 37 | 38 | def __init__(self, *args, **kwargs): 39 | pass 40 | print("After subclassing: ") 41 | for k in RegistryHolder.REGISTRY: 42 | print(k) 43 | 44 | ### OUTPUT ### 45 | # Before subclassing: 46 | # BaseRegisteredClass 47 | # After subclassing: 48 | # BaseRegisteredClass 49 | # ClassRegistree 50 | -------------------------------------------------------------------------------- /specification.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @author: Gordeev Andrey 6 | 7 | Specification provide recombination business logic by 8 | chaining together using boolean logic 9 | """ 10 | 11 | from abc import abstractmethod 12 | 13 | 14 | class Specification(object): 15 | 16 | def and_specification(self, candidate): 17 | raise NotImplementedError() 18 | 19 | def or_specification(self, candidate): 20 | raise NotImplementedError() 21 | 22 | def not_specification(self): 23 | raise NotImplementedError() 24 | 25 | @abstractmethod 26 | def is_satisfied_by(self, candidate): 27 | pass 28 | 29 | 30 | class CompositeSpecification(Specification): 31 | @abstractmethod 32 | def is_satisfied_by(self, candidate): 33 | pass 34 | 35 | def and_specification(self, candidate): 36 | return AndSpecification(self, candidate) 37 | 38 | def or_specification(self, candidate): 39 | return OrSpecification(self, candidate) 40 | 41 | def not_specification(self): 42 | return NotSpecification(self) 43 | 44 | 45 | class AndSpecification(CompositeSpecification): 46 | _one = Specification() 47 | _other = Specification() 48 | 49 | def __init__(self, one, other): 50 | self._one = one 51 | self._other = other 52 | 53 | def is_satisfied_by(self, candidate): 54 | return bool(self._one.is_satisfied_by(candidate) and 55 | self._other.is_satisfied_by(candidate)) 56 | 57 | 58 | class OrSpecification(CompositeSpecification): 59 | _one = Specification() 60 | _other = Specification() 61 | 62 | def __init__(self, one, other): 63 | self._one = one 64 | self._other = other 65 | 66 | def is_satisfied_by(self, candidate): 67 | return bool(self._one.is_satisfied_by(candidate) or 68 | self._other.is_satisfied_by(candidate)) 69 | 70 | 71 | class NotSpecification(CompositeSpecification): 72 | _wrapped = Specification() 73 | 74 | def __init__(self, wrapped): 75 | self._wrapped = wrapped 76 | 77 | def is_satisfied_by(self, candidate): 78 | return bool(not self._wrapped.is_satisfied_by(candidate)) 79 | 80 | 81 | class User(object): 82 | 83 | def __init__(self, super_user=False): 84 | self.super_user = super_user 85 | 86 | 87 | class UserSpecification(CompositeSpecification): 88 | 89 | def is_satisfied_by(self, candidate): 90 | return isinstance(candidate, User) 91 | 92 | 93 | class SuperUserSpecification(CompositeSpecification): 94 | 95 | def is_satisfied_by(self, candidate): 96 | return getattr(candidate, 'super_user', False) 97 | 98 | 99 | if __name__ == '__main__': 100 | print('Specification') 101 | andrey = User() 102 | ivan = User(super_user=True) 103 | vasiliy = 'not User instance' 104 | 105 | root_specification = UserSpecification().\ 106 | and_specification(SuperUserSpecification()) 107 | 108 | print(root_specification.is_satisfied_by(andrey)) 109 | print(root_specification.is_satisfied_by(ivan)) 110 | print(root_specification.is_satisfied_by(vasiliy)) 111 | 112 | 113 | ### OUTPUT ### 114 | # Specification 115 | # False 116 | # True 117 | # False 118 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------