Bob Nadler, Jr. Bob Nadler, Jr.

Delegation in Python

Published about 10 years ago 1 min read
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

    @property
    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
        requests.models.Response.

        """
        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.


Share This Article