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.

2D Land version 1.0

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"

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.

2D Land version 2.0

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

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

    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:

Flattening the inheritance hierarchy

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

from dog import Dog

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

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

    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

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"

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"

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.

6 comments to The Adapter Pattern in Python

  • Martijn Faassen

    Cool explanation of this pattern!

    You can expand the pattern by implementing a factory function for your adapters. Imagine you have two noise adapters that implement the “make_noise” method for cats and dogs:

    class CatNoise(object):
       def __init__(self, context):
            self.context = context
    
       def make_noise(self):
            return self.context.meow()
    
    class DogNoise(object):
        def __init__(self, context):
            self.context = context
    
       def make_noise(self):
            return self.context.bark()

    You could now write a factory function that can handle both cats and dogs:

    def noise_adapter(context):
         if isinstance(context, Cat)
              return CatNoise(context)
         elif isinstance(context, Dog):
              return DogNoise(context)
         else:
              raise AdapterLookupError("Could not find adapter")

    Now you can just do this when you have a list of cats and dogs:

       animals = [Cat('Jones'), Dog('Whoof'), Dog('Andy')]
       for animal in animals:
           print noise_adapter(animal).make_noise()

    Thanks to this noise_adapter factory function you can get a noise_adapter for any animal that the factory function knows about.

    It’d be nice to make the noise_adapter factory function pluggable some way. You could for instance have some kind of dictionary that maps classes to the particular adapters. Let’s look at that:

    noise_adapter_lookup = {Cat: CatNoise, Dog: DogNoise}
    
    def noise_adapter(context)
       try:
           return noise_adapter_lookup[context.__class__](context)
       except KeyError:
           raise AdapterLookupError

    By modifying noise_adapter_lookup you could now expand what animals noise_adapter can adapt without having to modify its code.

    That gets me to zope.interface and zope.component, which are libraries that help you implement this pattern.

    zope.interface is a library that you can use to define the concept of a the “noise” interface more explicitly:

    from zope.interface import Interface
    
    class INoiseMaker(Interface):
        def make_noise():
            "Return the particular noise this animal makes."

    This simply describes what the API is of a noise maker, like our adapters. The DogNoise and CatNoise adapters both implement INoise.

    zope.component now allows you to register adapters for cats and dogs, effectively filling a registry (like the dictionary above, but smarter with awareness of inheritance and such) with this information:

    from zope import component
    
    component.provideAdapter(DogNoise, adapts=Dog, provides=INoise)
    component.provideAdapter(CatNoise, adapts=Cat, provides=INoise)

    What you can do now is this with the INoise interface:

    print INoise(my_cat).make_noise()
    print INoise(my_dog).make_noise()

    Calling INoise on something will effectively mean “give me something that provides INoise for this particular object”. This is aware of inheritance so that it’ll work for subclasses of Dog and Cat as you’d expect.

    Doing these manual ‘component.provideAdapter’ registrations can be annoying. You can use a library called grokcore.component that builds on zope.component and zope.interface to automate this so you can just write your adapters like this (after a bit of setup in your application when it starts):

    from grokcore import component
    
    class CatNoise(component.Adapter)
        component.provides(INoise)
        component.context(Cat)
    
        def make_noise(self):
            return self.context.meow()

    As you can see this is only a minor variation of the CatNoise adapter above, adding a few directives that tell it how it is to be registered. I’ll leave the implementation of the DogNoise adapter to your imagination.

    To conclude, with grokcore.component you can write your adapters and have them be looked up dynamically as described before without having to do any registrations yourself.

    I hope this expansion on the subject was useful.

  • Martijn Faassen

    I carefully put in all the in my comment to try to format the source code comments above, as your blog said “HTML encouraged”, but it seems to have swallowed them up whole. That’s a bit unfortunate as it makes the code samples rather hard to read. Any chance you could fix this up somehow?

    Earlier I wrote a similar explanation to what I did above, here:

    http://grok.zope.org/doc/current/grok_overview.html#adapters

  • Martijn Faassen

    I meant to say put the “p r e” in my comments but either I typoed that or it got swallowed too by your evil comment engine. :)

  • @Martijn

    Thanks for the code! I went in and stuck in the <pre> tags for you. I’m not sure why it didn’t work when you did it, since I’m able to add pre tags without problems. I guess I’ll have to poke around in the WordPress code to find out what’s going on (not a fun activity…).

    Test pre tags
  • Eli

    A nice explanation, thanks. I’m on a quest of understanding whether Interfaces are useful in Python, and adapters are one of the reasons they are – they allow cleaner integration between components that aren’t exactly suitable for each other.

    And by the way, the ABC feature (Abstract Base Class) is available for Python 2.6, AFAIK.

  • Excellent explanation of Adapter for python! I refer to this article in my blogpost on using a resource adapter for model objects in pyramid traversal.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>