The Adapter Pattern in Python

Interface mismatches are one of the banes of code reuse. You want to integrate component A and component B into your program, but component A expects a method called half_empty() while component B only has a method called half_full(). The Gang of Four (GoF) Adapter pattern is used to convert the interface of an object to one the client expects, allowing collaboration between classes that normally couldn't work together due to differing interfaces.

2D Land: A Demonstration of the Adapter Pattern

Let's motivate that with a little example. We're going to start with a simple system, then add components to it with an interface that's different from the one our system expects. We'll use the Adapter pattern to wrap the new components, so we can use them without changing the code in our existing system.

So let's get started. We've been tasked with writing the back-end code for 2D Land, a cutting-edge world simulation program involving poorly drawn cartoon figures. Our initial spec is pretty simple: our world consists of people, and when a person is clicked, their name and a speech bubble is printed. We need to code a Person class, and a click_creature() function that will return a person's name and speech bubble to the GUI team.

So let's get coding. One way to code this would be as shown below. We have a Person class, with a name property and a make_noise() method. These can be accessed to retrieve the Person's name and speech bubble.

class Person ( object ) :

"" "A representation of a person in 2D Land" ""

def __init__ ( self , name ) :

self . name = name

def make_noise ( self ) :

return "hello" Person, name= namemake_noise def click_creature(creature):

"""

React to a click by retrieving the creature's

name and what is says

""" return creature.name, creature.make_noise()

The Person class takes a name argument, which it stores in its name attribute. The click_creature() function retrieves the person's name and speech-bubble text. We test it, and everything works as expected. Looks like it's time for a launch party!

More Features in 2D Land

After we get back from our team trip to Cancun, we find that our users are so happy with 2D Land that management has decided to build 2D Land version 2.0! The killer new feature of version 2.0 is that in addition to people, 2D Land will now also have dogs.

Luckily, the programmers over in the Pet Land team are going to let us use their Dog class. The Dog class has a name property, and a bark() method that returns the dog's bark sound.

class Dog ( object ) :

"" "A representation of a dog in 2D Land" ""

def __init__ ( self , name ) :

self . name = name

def bark ( self ) :

return "woof"

As it stands, the Dog class doesn't have the interface that our clients expect. It has a name property, but instead of a make_noise() method, it's got a bark() method. But we can use the Adapter pattern to wrap the Dog class and give it the expected interface.

Let's start by solving this the classic GoF way. There are actually two ways to implement Adapter in GoF: the Class Adapter and the Object Adapter. The Class Adapter uses multiple inheritance, while the Object Adapter uses encapsulation. For the sake of completeness I'm going to show both of them, and then explain why the Object Adapter is almost always the best choice for Python.

According to the classic implementation of the Adapter pattern, we need a common class that Person and a dog adapter can inherit from; let's call it a Creature class. The DogClassAdapter and DogObjectAdapter classes will each inherit from Creature; DogClassAdapter will then inherit from Dog, and DogObjectAdapter will wrap Dog.

According to the pattern, Creature is supposed to be an abstract base class (ABC); that is, it's not supposed to be instantiated directly. Python 2.X doesn't have ABCs as a language feature, but we can fake it here by having calls to make_noise() raise a NotImplementedError. This means that we need classes to inherit from Creature and implement make_noise() for us.

from dog import Dog dogDog class Creature(object):

"""The base class for creatures in 2D Land""" def make_noise(self):

"""

This is a technique to fake an ABC

in Python 2.X

"""

raise NotImplementedError class Person(Creature):

"""A representation of a person in 2D Land"""

def __init__(self, name):

self.name = name def make_noise(self):

return "hello" class DogClassAdapter(Creature, Dog):

"""Adapts the Dog class through multiple inheritance"""

def __init__(self, name):

Dog.__init__(self, name) def make_noise(self):

"""

Provide the 'make_noise' method that

the client expects

"""

return self.bark()

DogClassAdapter inherits from Creature and Dog, getting the functionality of both. It takes a name parameter in its __init__() method, which it passes on to Dog. It then implements a make_noise() method, returning the results of the bark() method it inherited from Dog.

class DogObjectAdapter ( Creature ) :

"" "Adapts the Dog class through encapsulation" ""

def __init__ ( self , canine ) :

self . canine = canine DogObjectAdapterCreature, canine= canine def make_noise(self):

"""This is the only method that's adapted"""

return self.canine.bark() def __getattr__(self, attr):

"""Everything else is delegated to the object"""

return getattr(self.canine, attr)

DogObjectAdapter only inherits from Creature. It takes a Dog instance as a parameter to its __init__() method, and stores that instance as self.canine. It implements a make_noise() method, which returns the result of calling the bark() method of its wrapped Dog object. All other calls on the class are passed to its canine instance via the __getattr__() method.

In both cases, we only need to implement the interface we're adapting. DogClassAdapter inherits the rest of the Dog interface, while DogObjectAdapter uses __getattr__() to provide the same interface as the Dog class, with the exception of the methods it implements itself.

And here's why we don't need the Class Adapter. Class Adapter is basically a shortcut to avoid coding stubs. In languages like C++, Object Adapter would force us to implement stubs for every method on the Dog class, just so we could pass them on to the wrapped Dog instance. With Class Adapter, however, we'd already have the behaviour implemented, so wouldn't need to write stubs. This doesn't make any difference in a simple class like Dog, but consider a huge class like StringIO.StringIO from the standard library; writing stubs for every method in that class just to adapt the interface slightly would be a real pain. But in Python, we don't have to write stubs for the behaviour we don't wrap; we can take advantage of dynamic dispatching instead, by implementing the __getattr__() method.

__getattr__() is a "magic" method, as denoted by the double underscores. The Python interpreter calls it when it does an attribute lookup on the object, and can't find a matching attribute. By intercepting this call and passing on the attribute lookup to the wrapped object, DogObjectAdapter only needs to implement the make_noise() method; everything else gets passed on to its wrapped object.

Flattening the Object Hierarchy

The code above works; we can created adapted dogs, and feed them into our click_creature() function. But is inheriting from a Creature base class the best way? With statically typed languages, inheritance is used for two purposes: to coerce types, and to inherit behaviour. By coercing types, I mean using a common base class so that we can feed a common type to the click_creature() function. These are the kinds of hoops you need to jump through to make the compiler happy in statically typed languages. In Python, we don't need to use inheritance to coerce the type system, because we use duck typing; we only need to use inheritance for behaviour.

Duck typing: ask what an object can do, rather than ask what type an object is (i.e. don't ask if it's a duck; ask if it quacks like a duck).

So we don't need to inherit from a Creature base class in order to use our various objects in the click_creature() function. And in fact, there's a big problem with this inheritance approach: it would force us to change our existing codebase, by modifying the Person class to inherit from Creature. So let's flatten the hierarchy out:

Here's our code without the Creature base class, and getting rid of the Class Adapter:

from dog import Dog dogDog class Person(object):

"""A representation of a person in 2D Land"""

def __init__(self, name):

self.name = name def make_noise(self):

return "hello" class DogAdapter(object):

"""Adapts the Dog class through encapsulation"""

def __init__(self, canine):

self.canine = canine def make_noise(self):

"""This is the only method that's adapted"""

return self.canine.bark() def __getattr__(self, attr):

"""Everything else is delegated to the object"""

return getattr(self.canine, attr) def click_creature(creature):

"""

React to a click by showing the creature's

name and what is says

""" return (creature.name, creature.make_noise())

This is a lot simpler. We've captured the intent of the Adapter pattern, without bothering with the trappings of less dynamic languages.

Just because the GoF implement a pattern in a certain way doesn't mean it's the only or even best way to do it in Python. Use the essence of the pattern, and leave out the accidents of implementation.

Testing out the System

Let's test out our DogAdapter class with a function that simulates clicks on each type of creature in 2D Land.

from dog import Dog

from listing3 import Person, DogAdapter dogDoglisting3Person, DogAdapter def exercise_system():

person = Person("Bob")

canine = DogAdapter(Dog("Fido")) for critter in (person, canine): print critter.name, "says", critter.make_noise() if __name__ == "__main__":

exercise_system()

animate_objects() creates instances of classes Person and DogAdapter; it then prints the name and speech bubbles of each object. The output is shown below.

Output:

Bob says hello

Fido says woof

Perfect! Looks like we're ready to launch version 2.0.

Extending the Adapter Concept

We've got our Adapter working pretty well now, but we can still make it better. Think about how we added a Dog class to 2D Land. What if we later want to add a Cat class, and Bird class, and so on? Here's a Cat class, that has a name attribute, and a meow() method that returns the cat's meow sound.

class Cat ( object ) :

"" "A representation of a cat in 2D Land" ""

def __init__ ( self , name ) :

self . name = name

def meow ( self ) :

return "meow"

The problem with the current implementation is that every time we add a new class to our system, we're potentially going to have to write a new adapter class for it.

We can take advantage of Python's dynamic nature to write a single, generic adapter class for all the new classes we add. Our generic adapter will take two arguments in its __init__() method: the object it's going to wrap, and an implementation of make_noise().

class CreatureAdapter ( object ) :

"" "Adapts a creature for clients in 2D Land" ""

def __init__ ( self , creature, make_noise ) :

"" "Pass in the function to use as 'make_noise'" ""

self . creature = creature

self . make_noise = make_noise CreatureAdapter, creature, make_noise= creature= make_noise def __getattr__(self, attr):

"""Everything else is delegated to the object"""

return getattr(self.creature, attr)

Note that the code no longer has to know anything about the Dog class or other creatures in 2D land. All we need to do is supply an object to adapt and a method to use for make_noise(), and we're done.

So let's test out the new system, and see how we'd specify the make_noise() method.

from dog import Dog

from cat import Cat

from twodeeland import Person, CreatureAdapter dogDogcatCattwodeelandPerson, CreatureAdapter def exercise_system():

person = Person("Bob")

fido = Dog("Fido")

canine = CreatureAdapter(fido, fido.bark)

whiskers = Cat("Whiskers")

feline = CreatureAdapter(whiskers, whiskers.meow) for critter in (person, canine, feline):

print critter.name, "says", critter.make_noise()

We have to specify which method to use as the make_noise() method on instance creation, but in return we no longer have to create an Adapter for each new class in the system. I'd say that's a big win.

Output:

Bob says hello

Fido says woof

Whiskers says meow

One final note about this system: you may have been wondering what happens if the class we're adapting doesn't have a name attribute (for example, if the attribute is called "nickname"). In that case, we could simply create new class that inherits from the creature class, implementing the name attribute via a property, and pass an instance of that to our CreatureAdapter class.

class NicknameCat ( object ) :

"" "A representation of a cat in 2D Land" ""

def __init__ ( self , nickname ) :

self . nickname = nickname

def meow ( self ) :

return "meow" NicknameCat, nickname= nicknamemeow class NicknameCatAdapter(NicknameCat):

def __init__(self, name):

NicknameCat.__init__(self, name)

def _get_name(self):

return self.nickname

def _set_name(self, name):

self.nickname = name

name = property(_get_name, _set_name)

We could then do something like this:

kitty = NicknameCatAdapter ( "Whiskers" )

critter = CreatureAdapter ( kitty, kitty. meow )

Note that this presupposes that the adapted class has some functionality that serves as the name attribute; the Adapter class is all about changing the interface of an object, not adding to its functionality.

Using Adapter with Objects Other Than Classes

The Adapter pattern as classically implemented works with classes, but that's just an accident of the languages that the pattern was initially made for. Essentially any object in Python is ripe for adapting. For example, you could adapt a function so that its signature was something that the client expected.

As an example, below we have a countdown() function. It takes as its arguments a number to count down from, and a callback function to call when it's done (callbacks are a common programming technique in things like multithreaded and asynchronous programs). We want to use the completed() function as its callback, but completed() takes an argument (the source), while countdown() calls its callback without any arguments. We can use the adapter pattern to change the signature of completed() to what countdown() expects.

def completed ( source ) :

print source, "completed" completedsourcesource, def countdown(num, callback):

for i in range(num, 0, -1):

print "%s…" % i

callback() def main():

def nullarg_callback():

completed("countdown")

countdown(3, nullarg_callback) if __name__ == "__main__":

main()

Above, we adapted the completed() function using the nullarg_callback() function.

Output:

3…

2…

1…

countdown completed

The Adapter pattern changes the interface of an object in order to match the interface that's expected. In the 2D Land example, we adapted the Dog class to provide make_noise() method that our system expects. The Adapter pattern is a good choice any time you're integrating some code into an existing system, and the code has the needed functionality, but not the needed interface.

Here's the code for this post, together with unit tests.