My solution to the localization horror story Maximizing your income as a translator » Six GoF design patterns, Python style Although it usually requires some adaptation, Python makes implementing most of the GoF design patterns trivial. In some cases, they're actually part of the core language; in most of the others, they're a lot simpler in Python than C++ et al. Here I have implemented examples of the following common design patterns in Python: Iterator

Decorator

Abstract factory

Factory

State

Template Please see the links below for the Wiki articles describing each pattern. For now, I'll punt on when design patterns should be used. Iterator Iterators are built into the Python language. Anything that is iterable can be iterated, including dictionaries, lists, tuples, strings, streams, generators, and classes for which you implement iterator syntax. This example shows a generator that counts by number words. "" "Implementation of the iterator pattern with a generator" "" def count_to(count):

"""Counts by word numbers, up to a maximum of five""" numbers = ["one", "two", "three", "four", "five"]

# The zip keeps from counting over the limit

for number, pos in zip(numbers, range(count)):

yield number # Test the generator

count_to_two = lambda : count_to(2)

count_to_five = lambda : count_to(5) print "Counting to two…"

for number in count_to_two():

print number,

print "

"

print "Counting to five…"

for number in count_to_five():

print number, Output:

one two Counting to two…one two Counting to five…

one two three four five Decorator This one is another gimme — decorators have built-in syntax since Python 2.4 (they're still trivial to implement in earlier versions). This example shows a time_this decorator, which measures and prints the time it takes the decorated function to run, in seconds. import time def time_this(func):

"""The time_this decorator""" def decorated(*args, **kwargs):

start = time.time()

result = func(*args, **kwargs)

print "Ran in", time.time() – start, "seconds"

return result

return decorated # Decorator syntax

@time_this

def count(until):

"""Counts to 'until', then returns the result""" print "Counting to", until, "…"

num = 0

for i in xrange(to_num(until)):

num += 1

return num def to_num(numstr):

"""Turns a comma-separated number string to an int"""

return int(numstr.replace(",", "")) # Run count with various values

for number in ("10,000", "100,000", "1,000,000"):

print count(number)

print "-" * 20 Output: Counting to 10,000 …

Ran in 0.0159997940063 seconds

10000

——————–

Counting to 100,000 …

Ran in 0.0160000324249 seconds

100000

——————–

Counting to 1,000,000 …

Ran in 0.233999967575 seconds

1000000

——————– Although the built-in decorator syntax is for functions and methods, it's also very simple to decorate classes in Python. Here's a simple output-stream decorator I wrote. Abstract factory This pattern is a lot simpler to implement than the GoF example, because there's no need to inherit just to satisfy types. In the example below, the PetShop class has an abstract factory as a member ( pet_factory ). We can configure it at runtime with the desired concrete factory. The pet shop will then generate the appropriate pet type depending on its factory. "" "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.""" self.pet_factory = animal_factory def show_pet(self):

"""Creates and shows a pet using the

abstract factory""" pet = self.pet_factory.get_pet()

print "This is a lovely", pet

print "It says", pet.speak()

print "It eats", self.pet_factory.get_food() # Stuff that our factory makes class Dog:

def speak(self):

return "woof"

def __str__(self):

return "Dog" class Cat:

def speak(self):

return "meow"

def __str__(self):

return "Cat" # Factory classes class DogFactory:

def get_pet(self):

return Dog()

def get_food(self):

return "dog food" 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!"""

return random.choice([DogFactory, CatFactory])() # Show pets with various factories

shop = PetShop()

for i in range(3):

shop.pet_factory = get_factory()

shop.show_pet()

print "=" * 10 Sample output: This is a lovely Dog

It says woof

It eats dog food

==========

This is a lovely Cat

It says meow

It eats cat food

==========

This is a lovely Cat

It says meow

It eats cat food

========== Factory We don't need our factory products to have a common base class in order to give them a common type, but we can if we want to in order to share functionality. In this example, we have a factory function called get_localizer , which returns the appropriate localizer class depending on the language name we pass to it. #coding: UTF8 class JapaneseGetter:

"""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""" try:

return unicode(self.trans[msgid], "utf-8")

except KeyError:

return unicode(msgid) class EnglishGetter:

"""Simply echoes the msg ids"""

def get(self, msgid):

return unicode(msgid) def get_localizer(language="English"):

"""The factory method""" languages = dict(English=EnglishGetter,

Japanese=JapaneseGetter) return languages[language]() # Create our localizers

e, j = get_localizer("English"), get_localizer("Japanese") # Localize some text

for msgid in "dog parrot cat".split():

print e.get(msgid), j.get(msgid) Output: dog 犬

parrot parrot

cat 猫 State The state pattern allows us to change an object's behavior at runtime — something that Python naturally excels at! This example has a very simple radio. It has an AM/FM toggle switch, and a scan button to scan to the next station. "" "Implementation of the state pattern" "" class State(object):

"""Base state. This is to share functionality""" def scan(self):

"""Scan the dial to the next station"""

self.pos += 1

if self.pos == len(self.stations):

self.pos = 0

print "Scanning… Station is", self.stations[self.pos], self.name class AmState(State):

def __init__(self, radio):

self.radio = radio

self.stations = ["1250", "1380", "1510"]

self.pos = 0

self.name = "AM" def toggle_amfm(self):

print "Switching to FM"

self.radio.state = self.radio.fmstate class FmState(State):

def __init__(self, radio):

self.radio = radio

self.stations = ["81.3", "89.1", "103.9"]

self.pos = 0

self.name = "FM" def toggle_amfm(self):

print "Switching to AM"

self.radio.state = self.radio.amstate 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)

self.fmstate = FmState(self)

self.state = self.amstate def toggle_amfm(self):

self.state.toggle_amfm()

def scan(self):

self.state.scan() # Test our radio out

radio = Radio()

actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2

actions = actions * 2

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 Template The template pattern allows us to abstract away common parts of an algorithm. We simply supply a template skeleton with concrete actions. In Python, functions are first-class objects, so we can create template functions via composition rather than classes and inheritance as shown in the GoF. In this example, we build functions that iterate over a sequence, performing some action on each element; we fill in a skeleton function with a getter that retrieves the sequence, and an action that acts on each element. "" "An example of the Template pattern in Python" "" # Skeletons

def iter_elements(getter, action):

"""Template skeleton that iterates items""" for element in getter():

action(element)

print "-" * 10 def rev_elements(getter, action):

"""Template skeleton that iterates items in reverse order""" for element in getter()[::-1]:

action(element)

print "-" * 10 # Getters

def get_list():

return "spam eggs".split() def get_lists():

return [list(x) for x in "spam eggs".split()] # Actions

def print_item(item):

print item def reverse_item(item):

print item[::-1] # Makes templates

def make_template(skeleton, getter, action):

"""Instantiate a template method with getter and action"""

def template():

skeleton(getter, action)

return template # Create our template functions

templates = [make_template(s, g, a)

for g in (get_list, get_lists)

for a in (print_item, reverse_item)

for s in (iter_elements, rev_elements)] # Execute them

for template in templates:

template() As you can see, it's quite easy to alternate among both concrete functions and skeletons. Output: spam

eggs

———-

eggs

spam

———-

maps

sgge

———-

sgge

maps

———-

['s', 'p', 'a', 'm']

['e', 'g', 'g', 's']

———-

['e', 'g', 'g', 's']

['s', 'p', 'a', 'm']

———-

['m', 'a', 'p', 's']

['s', 'g', 'g', 'e']

———-

['s', 'g', 'g', 'e']

['m', 'a', 'p', 's']

———- Conclusion It's fairly straightforward to adapt most of the GoF patterns to Python — and they end up a lot simpler in the bargain! Edit: Modified abstract factory to match "classic" pattern. Edit 2: Implemented new iterator and state patterns. Leave a Reply