Python Patterns 1
Brandon Rhodes
PyOhio
29 July 2012
 
 
Python Patterns 1
The “1” indicates my first try
at tackling a large subject!
 
(I might have more
to say in the future)
 
 
Why do Python
programmers rarely talk
about design patterns?
 
 
Why do Python
programmers rarely talk
about design patterns?
 
In his 2009 talk at PyCon,
Joe Gregorio suggested:
 
- “The patterns are built in” to Python
- Concurrent patterns need more attention
- Missing patterns could be new features
 
The gist of this talk—
The gist of this talk
Why do Python
programmers rarely talk
about design patterns?
 
- Built-in language features
- Are provided by frameworks
- We avoid big applications
 
What is a pattern?
Two books…
 
Christopher Alexander
(1977)
 
 
Elements of Reuseable
Object-Oriented Software
 
Gamma, Helm, Johnson, Vlissides
“The Gang of Four”
(1995)
 
 
Not all patterns are
object-oriented design patterns
 
OO Design Patterns  ⊂  Patterns
 
 
Patterns in Python exist
at many different levels
 
 
Small procedural pattern
if 'owner' in roledict:
    owner = roledict['owner']
else:
    owner = admin
(-1) for looking up 'owner' twice
(-1) requires four lines and an indent
 
owner = roledict.get('owner', admin)
 
Bigger procedural pattern
for item in items:
    if is_for_sale(item):
        cost = compute_cost(item)
        if cost <= wallet.money:
            buy(item)
(-1) actual buy() is buried 2 levels deep
 
for item in items:
    if not is_for_sale(item):
        continue
    cost = compute_cost(item)
    if cost > wallet.money:
        continue
    buy(item)
 
Object-Oriented design patterns
are specifically about putting the
big pieces of an application together
 
- Classes
- Objects
- References
 
How can a language
feature replace a pattern?
 
 
1st principle in Design Patterns (p18):
“Program to an interface,
not an implementation.”
 
 
Problem: static typing locks you in
(there is only one class named File)
 
public void writeZen(File out) {
    out.write('Beautiful is better than ugly.');
}
Solution: do not mention actual types
(any class can implement IWritable)
 
public void writeZen(IWritable out) {
    out.write('Beautiful is better than ugly.');
}
 
Why would you do this?
Because someday you might want to write
to something that is file-like
but not an actual File!
 
 
public void writeZen(File out) {
    out.write('Beautiful is better than ugly.');
}
↑
Only usable for File subclasses
 
If your new WebSocket class
also offers IWritable then you
can re-use this method for the web!
↓
 
public void writeZen(IWritable out) {
    out.write('Beautiful is better than ugly.');
}
 
So, do we worry
about this in Python?
 
 
No
Why?
Because in Python we
throw static typing overboard
and instead use duck typing
 
def writeZen(out):
    out.write('Beautiful is better than ugly.')
This function accepts anything
and simply risks an exception
if the duck refuses to quack
 
 
“Python is dynamic”
and does not make you pre-specify
the types accepted by a function
 
—eliminates a whole GoF design principle!
 
 
In other words—
All Python code you have ever written
automatically adheres to the GoF principle
that functions should not name the
classes that can be passed in.
 
(Unless you add inflexibility
manually with isinstance() tests)
 
 
In general
Java and C++ programmers
maintain grueling and expensive
coding disciplines
 
to remove a set of handcuffs
that in Python quite simply
do not exist to begin with
 
 
Opinion
Is it a damning indictment of C++/Java
that their most fundamental and
persistent coding practices
 
all seem designed to make Java
more like writing in Python?
 
 
So,
we have seen how a Gang of Four
principle disappears in Python.
 
What about the patterns themselves?
 
 
- 5 Creational Patterns
- 7 Structural Patterns
- 11 Behavioral Patterns
 
The 5 Creational Patterns
Python is brilliant
No separate syntax for
object instantiation
 
 
# C++, Java, JavaScript
v = getvalue();   // plain function
t = new Thing();  // instantiation
# Python
t = getthing()    # everything!
 
In other languages the new keyword
commits you to doing a real instantiation,
which makes all kinds of things impossible:
 
- Returning another class
- Always return the same instance
 
def get_talker(s):
    if s.startswith('xmlrpc://'):
        return XMLRPC_Talker(s)
    else:
        return JSON_Talker(s)
 
So most “creational patterns”
are contortions to avoid making
brittle new calls in your C++/Java code
 
- 
- Returning another class - (Factory Method, Abstract Factory) 
 
- 
- Always return the same instance - (Singleton) 
 
 
In Python, you cannot tell
what is being called:
 
Is this a bound method?
A class with a lowercase name?
Or a plain old function?
 
It can be whatever
its author needed!
 
 
t = thing()
isinstance(t, thing)
(You can tell the difference if you
isinstance() with the constructor —
but you wouldn't do that, right?)
 
 
Notes about Python singletons
- 
- Using a global variable is bad, - because you can never switch in - something dynamic: mymodule.foo - can not be intercepted! 
 
 
Notes about Python singletons
- 
- So you should always make callers - actually invoke a function to get - your singleton; minimally: 
 - 
- # A kind-of singleton
_singleton = MyClass()
def get_singleton():
    return _singleton
 
 
Notes about Python singletons
- 
- But note that the real Singleton - pattern is supposed to let the - caller provide a subclass! 
 
 
_singleton = None
def get_singleton(cls=MyClass):
    global _singleton
    if _singleton is None:
        _singleton = cls()
    return _singleton
You can always make things
more complicated later:
 
- Hide _singleton inside a closure
- Hide _singleton inside a class
 
What if your users want a constructor
that is also the singleton's class,
so they can isinstance() it?
 
Then you have to roll up your sleeves
and give your class a __new__() method,
see Stack Overflow for examples
(and arguments against!)
 
 
Prototype
Another creational pattern is provided
not by the Python language itself,
but by the Standard Library:
 
The copy module can introspect
and make a copy of an object
 
 
Builder
The most complex creational
pattern is actually very useful:
 
a Builder receives instructions about
what to build, and hides the details
of the instances it links together
 
 
Making XML without a builder
We see the gory details
as instances are created
and then linked together
 
from lxml import etree
root = etree.Element('body')
h1 = etree.Element('h1')
h1.text = 'The Title'
root.append(h1)
p = etree.Element('p')
p.text = 'Always write Python'
root.append(p)
 
Making XML with a builder
Our code does not become laden
with any information about the
classes being instantiated!
 
from lxml.builder import E
doc = E('body',
        E('h1', 'The Title'),
        E('p', 'Always write Python'))
 
Builder
This pattern perhaps appears
in popular frameworks like lxml
more often than in user code
 
 
Creational Patterns in Python
Designed away by language:
Abstract Factory
Factory Method
 
Trivial:
Useful:
 
The 7 Structural Patterns
Structural Pattern: Adapter
Adapter
Wraps one class
so that it behaves
like another class
 
 
Adapter example
Sockets do not read
and write like files do
 
>>> import socket
>>> s = socket.socket()
>>> s.read
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: '_socketobject' object
 has no attribute 'read'
 
Instead, they “send” and “receive”
 
>>> s.send
<built-in method send>
>>> s.recv
<built-in method recv>
 
So the Standard Library provides
a socket._fileobject adapter that
translates file-like read() and write()
operations into send() and recv()
calls on the socket
 
>>> f = s.makefile()
>>> f.read
<bound method _fileobject.read>
 
Adapters are a crucial pattern
letting us re-use code in Python
 
Sometimes the day can be saved by
putting an object behind a file-like or
sequence-like adapter so it can be
passed to existing Python code
 
 
Structural Pattern: Bridge
Bridge
- 
- Says not to use subclassing for - two separate purposes in one class 
 
- 
MacPlainWindow   MacBorderedWindow
WinPlainWindow   WinBorderedWindow
LinuxPlainWindow LinuxBorderedWindow
 
- 
- Partly rationalized by limitations - of C++ (no mixins, much subclassing) 
 
 
Solution: another layer!
Have one class that translates
the Plain and Bordered ideas
into primitive window operations
 
Have another class that can
perform these operations under
Windows, Mac, and Linux
 
 
This principle actually applies
at many levels in Python!
 
 
Gary Bernhardt at PyCon 2012
Q: My models are full of business
logic — how can I write simple tests
that don't write to the database?
 
class Person(Database_Model):
    def rename(self, new_name): ...
    def direct_deposit(self, amount): ...
 
Gary Bernhardt at PyCon 2012
A: Have models that do nothing but
persist information to storage
 
write a business logic layer
that implements the operations
that were expressed in methods
 
 
The Bridge pattern is actually a
good idea even at the level of modules
 
My modules often tackle only
a single level of impedance
match, instead of trying
to take several steps
 
 
# foo.py
CherryPy plugin → thread → library
# foo.py
CherryPy plugin → stdlib threading
# bar.py
thread → library
 
This leads to a Brandon rule:
If a module imports three major libraries
or frameworks, see if you can refactor it
into two modules, each of which only
imports two libraries
 
Your main program can then
plug the pieces together in main()
 
 
Structural Pattern: Composite
A class whose instances
are designed to lock
together in a tree
 
 
Composition is a common pattern
- email.Message can have messages inside
- lxml.etree.Element lists child elements
 
Two common mechanisms:
# Store children in attributes
obj.attr = subobject
# Store a list or dict of children
obj.children = [subobject, ...]
 
Structural Pattern: Facade
An object that hides a complex
tree or network of other objects
 
- 
- An etree Element lets you root.find() - and root.iter() which traverse the entire - tree of nodes for you 
 
 
Structural Pattern: Flyweight
Flyweight
A flyweight is a small object
that is immutable and can be
re-used in many contexts
 
 
A text box implemented without flyweights
 
textbox.chars = [c1, c2, c3, ...]
c1.letter = 'T'
c1.width = 9
c1.height = 10
c1.depth = 0
c1.outline = [(0, 10), (9, 10), ...]
c1.x = 0
c1.y = 14
c1.draw()
c2.letter = 'h'
⋮
c3.letter = 'e'
⋮
 
You have to create n instances
of the letter “T” so that each can
occupy a different x and y
 
c1.letter = 'T'
c1.width = 9
c1.height = 10
c1.depth = 0
c1.outline = [(0, 10), (9, 10), ...]
c1.x = 0
c1.y = 14
c1.draw()
 
The flyweight pattern moves this
per-object state out into the
parent or larger context
 
 
textbox.chars = [(cT, 0, 14),
                 (ch, 9, 14),
                 (ce, 18, 18),
                 (c_space, 27, 14),
                 (ch, 36, 14),
                 (ce, 45, 18),
                 ...]
cT.letter = 'T'
cT.width = 9
cT.height = 10
cT.depth = 0
cT.outline = [(0, 10), (9, 10), ...]
cT.draw(x, y)
 
Flyweight
- 
- A small set of objects can each - appear thousands of times inside - of parent objects 
 
- 
- But context like the x and y passed - to Character.draw(x, y) will now need - to be passed in to flyweight methods 
 
- 
- Saves memory at the expense of noise - and higher coupling 
 
 
Flyweight
C-Python uses the flyweight pattern
internally for integer objects
 
Since integer objects are immutable,
it only keeps a single copy of each small
integer like 0, 1, 2, ... and hands them
to your code over and over again
 
 
Structural Pattern: Proxy
Proxy
- 
- A proxy object wraps another object called - the subject and accepts method calls and - attribute lookups on its behalf 
 
- 
- Proxying can be performed dynamically - in Python with __getattr__() instead of - having to write n proxying methods 
 
 
Proxy
- 
- weakref.proxy(obj) returns a proxy 
 
- 
- Remote procedure call libraries offer - proxies for remote APIs, including the - Standard Library xmlrpc.client.ServerProxy - and also Pyro and rpyc 
 
- 
- The Zope web framework used proxies - that enforced security 
 
 
Structural Pattern: Decorator
Decorator
- 
- Like the Proxy, a Decorator class - offers the same attributes and methods - as the subject that it wraps 
 
- 
- But instead of being completely - transparent, it varies the behavior - or edits the data passing in and - out of the subject 
 
- 
- Decorators tend to appear in “glue” - code in applications, not inside - Python libraries themselves 
 
 
Note that none of the patterns
disappeared or proved useless simply
because our language is Python
 
 
Structural Patterns
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
 
The 11 Behavioral Patterns
Behavioral Pattern: Chain of responsibility
- 
- Pass a request along a chain of objects - until one of them decides to handle it 
 - def processClick(self, x, y):
    if self.active:
        self.buttonPress(x, y)
    else:
        self.next.processClick(x, y)
- 
- Used by GUIs and DOMs for mouse events 
 
- 
- Can be used by “help” feature: maybe button - has no help, but enclosing form does 
 
 
Behavioral Pattern: Command
Replaces immediate actions like:
paintLine(x1, y1, x2, y2)
Instead, you instantiate a command:
cmd = PaintLineCommand(x1, y1, x2, y2)
cmd.do()
command_history.append(cmd)
- Allows auditing and undo()
- Crucial to version control systems
 
Behavioral Pattern: Interpreter
Instead of compiling to machine language,
an interpreted language gets parsed into a
data structure that an interpreter program
iterates across, following the instructions
 
- 
- Data structure can be AST or bytecode 
 
- 
- Python itself is interpreted! 
 
- 
- Small domain-specific languages do sometimes - get written in Python to ease customization 
 
 
Behavioral Pattern: Iterator
- 
- Built-in because Python is awesome 
 
- 
- Introduced in 2001 — the most important - Python innovation of decade 
 
- 
- See Raymond Hettinger's PyCodeConf 2011 - talk “What Makes Python Awesome?” 
 
- 
- Lets you for x in obj: across your own - user-defined obj, or loop by hand - with i = iter(obj) and i.next() 
 
 
Two basic ways to implement iterator
 
 
First, you can create an actual class
to be your iterator that remembers:
 
- What it is iterating across
- Where it is in the sequence
 
Iterator
class Box(object):
    def __init__(self): self.things = [10, 20, 30]
    def __iter__(self): return BoxIterator(self)
class BoxIterator(object):
    def __init__(self, box):
        self.box = box
        self.index = -1
    def next(self):
        self.index += 1
        if self.index >= len(self.box.things):
            raise StopIteration()
        return self.box.things[self.index]
 
Or, you can simply make
__iter__() a generator
 
 
class Box(object):
    def __init__(self):
        self.things = [10, 20, 30]
    def __iter__(self):
        for thing in things:
            yield thing
 
Iterator
- 
- 
- Eliminates ugly mechanics of iteration - so code is more readable 
 
- 
- Enhances re-use because code is - easy to lock together in new ways; - again, see Raymond's talk! 
 
# JavaScript is littered with:
for (var i=0; i < box.length; i++) ...
 
Behavioral Pattern: Memento
- 
- A memento is a record of an object's - internal state — might be a string, or - file, or complex data structure 
 
- 
- Callers ask an object instance for a - memento, then can hand it back later - to ask the object to restore itself - to its earlier state 
 
- 
- Similar to pickle but instead of - creating a new object like un-pickling, - it changes an existing object 
 
 
Behavioral Pattern: Observer
- 
- Very popular pattern in GUIs and DOMs 
 
- 
- Display elements let the framework - know that they need to redraw when - specific model attributes change 
 
- 
- Your models stay simple and have - no knowledge of the big application - you have build around them, so long - as they signal a list of listeners - when an attribute changes 
 
 
Without Observer pub-sub, you get:
class MyModel(...):
    def set_total(self, number):
        self.total = number
        self.titlebar.update()
        self.graph.update()
        self.summary.update()
 
With Observer pub-sub, things are simpler:
class MyModel(...):
    def set_total(self, number):
        self.total = number
class MyTitlebar(...):
    def __init__(self, model):
        subscribe(model, 'total', self.redraw)
⋮
 
Observer rarely appears in Python
for reasons we will discuss shortly
 
 
Behavioral Pattern: State
        open()    close()
TCPStart → TCPOpen → TCPClosed
What we want to avoid:
if state == 'start':
    if action == 'open':
        ⋮
    elif action == 'close':
        ⋮
elif state == 'open':
    if action == 'open':
        ⋮
    elif action == 'close':
        ⋮
 
Behavioral Pattern: State
        open()    close()
TCPStart → TCPOpen → TCPClosed
- 
- Represent each state as a class - like TCPOpen or TCPClosed 
 
- 
- Give each class one method for - each transition like open() - and close() that returns the - next state 
 
- 
- Avoids huge nested if-then's, but - simple state machines can be a dict - in Python instead 
 
 
Behavioral Pattern: Strategy
- 
- Parametrize a big process by passing in - an object that specifies custom behavior 
 
- 
- Python has first-class functions so we - often just pass callbacks instead 
 
- 
- A simple example of the Strategy pattern - is the key= function that we pass to - sorted() and list.sort() 
 
 
Behavioral Pattern: Template
- 
- Give your classes empty methods like - aboutToStart() and justStarted() - that a subclass can customize 
 
- 
- The original author of threading in - the Standard Library intended callers to - subclass (!) Thread and provide their - own, more interesting run() method 
 
- 
- In Python we often pass a callable instead, - or — when there are many callbacks — pass - a Strategy object instead 
 
 
Behavioral Pattern: Visitor
- 
- Replaces doThis() and doThat() methods on - tree nodes, that act on the current node and - then call the same method on their children, - with a do(action) traversal method 
 
- 
- Not frequently encountered in Python 
 
- 
- We tend to turn this problem inside out: 
 - for node in lxml_root.walk():  # or os.walk!
    # do something to the node
 
Behavioral Patterns
- 
- Are big solutions for big problems 
 
- 
- Most patterns work fine in Python 
 
- 
- Many of them turn up in big and - popular libraries, or even in the - Standard Library 
 
- 
- The Iterator pattern is now a - foundation of how we write Python 
 
 
Conclusions
- 
- Creational patterns do tend to partly - disappear in Python 
 
- 
- But the Structural patterns are often - good ways to structure your code 
 
- 
- Most Behavioral patterns remain useful - but some can be collapsed into callbacks - or data structures that hold functions 
 
 
Bonus Round
Dependency Injection
The Problem
def a(): ... b() ...
def b(): ... c() ...
def c(): pass
- 
- Hard to re-use a() in situations - where you want to do something else - instead of doing b() 
 
- 
- Hard to test a() in isolation 
 
- 
- Expensive to test a() without - Foord's mock.patch() if b() or c() - does something expensive like talking - to a database or writing a file 
 
 
The Problem
A simple, concrete example:
def google_search(query_text):
    ⋮
    urllib.urlopen(url)
    ⋮
- Hard to test
- Hard to re-use
 
Dependency Injection
DI says we can pass the callable
that performs the “next action down”:
 
def google_search(query_text, urlopen):
    ⋮
    response = urlopen(url)
    ⋮
- In production, pass urllib.urlopen
- In tests, just pass a fake function
- Easy to re-purpose in the future
 
Problem
This can get annoying
big_function(
    query_text,
    urlopen=urllib.urlopen,
    listdir=os.listdir,
    stat=os.stat,
    link=os.link,
        ⋮
    ):
    ⋮
 
Grouping dependent functions into
objects can make it a bit simpler
 
big_function(
    query_text,
    url_utils,
    os_utils,
    ):
    ⋮
 
But can we avoid passing so
many pieces to begin with?
 
 
Another Approach
“Return of Control”
But what if you just did this?
 
def google_search(query_text):
    ⋮
    return url
def google_parse(response):
    ⋮
    return result
def main():
    url = google_search(query_text)
    response = urlopen(url))
    result = google_parse(response)
 
This can result in simple functions
and methods that are easy to test
 
and simple top-level code
that simply assembles Lego pieces
and then sets them running
 
 
Guidelines
- 
- I do this to application subsystems, - but I do not make the entire - application a huge function that - thinks dozens of pieces together! 
 
- 
- I often pass my own subsystems as - parameters, but give myself permission - to use Standard Library calls without - insisting on DI — Foord mock FTW 
 
 
End of Bonus Round
So, Patterns
Most patterns are still
a good idea in Python
 
Why don't we use them more?
 
 
My claim:
We rarely meet the bigger patterns
 
not because of any feature
or magic of the Python langauge
 
because of the kind of application
that Python programmers tend to write
 
 
Reason #1
Python programs tend to perform
 
small
lightweight
one-shot
 
 
- Answering a web request
- Transforming a text file
- Searching through log files
- Operate on a database table
- Doing a Hadoop reduce() step
At the end of each of these tasks,
you throw away your objects and start over
 
This is the Unix philosophy
of small targeted tools
 
 
Reason #2
When you do write
a large complex long-running
application in Python, your framework
already implements the relevant patterns
 
 
- Pyglet
- PyGame
- Tkinter
- PySide / PyQt
- Twisted
 
Again:
We rarely meet the bigger patterns
 
not because of any feature
or magic of the Python langauge
 
because of the kind of application
that Python programmers tend to write
 
We tend to write glue
because the Open Source community
have done the hard parts for us!
 
 
Thank you!
Some earlier work:
- Vespe Savikko (1997) (academic)
- Bruce Eckel (2001) (unfinished)
- Greg Sullivan (2002) (not Python)
- Alex Martelli (2007)