Bob Nadler, Jr. Logo

Delegation in Python

Shadow Stormtrooper vs. Storm Shadow
Image by JD Hancock

At my current client, we use Python. The other day I needed to wrap a class from the requests library, specifically a response object. I wanted to add a couple of custom methods to the class and have the other methods delegate to the original object in a clean way. Here's a simplified version of what I came up with:

class JSONResponse(object):
    """Wraps a requests.models.Response."""
    def __init__(self, response):
        self._response = response

    def content(self):
        """Assumes content is JSON and attempts to
        parse it. Raises an exception if it
        cannot be parsed.

        # ... parse JSON in self._response.content

    def __getattr__(self, name):
        """Delegate any methods not defined in
        JSONResponse to underlying

        return getattr(self._response, name)

The purpose of the wrapper class shown above is to cleanly handle HTTP responses that are encoded as JSON. The class defines a content property that attempts to parse the response content. Finally, the key to the pattern is the __getattr__ method. Whenever a JSONResponse object is sent a message, it first checks to see if it can respond. In the case of the content, it will respond with the result of our custom property. If we send it any other message, it will attempt to send it to the wrapped response object. If the wrapped response cannot respond to the message, then it will fail as normal.

Email list
Image by husin.sani

No spam, ever. Unsubscribe at any time.