Python: using getattr for properties and methods

Before we jump into how getattr can be a useful tool for DRYing up our code, lets cover how it works (docs here)! In short, it takes an object and a string version of a property name, and returns the value of that property, if it exists on an object. It can optionally take a default value to return if the attribute can’t be found on the object, and otherwise will return an AttributeError.

Okay, let’s look at an example, using a bare-bones class. Given the Person class as defined below:

class Person(object):    
    def __init__(self, first_name, last_name):        
        self.first_name = first_name        
        self.last_name = last_name    

    def say_hello(name):        
        print "Hello, {}!".format(name)

Okay, let’s get an instance of a Person:

adrienne = Person("Adrienne", "Domingus")

Once I’ve been instantiated, we can access my first_name (and last_name) attribute using the more familiar dot notation, but we can also get it using getattr, with the same result:

>>> adrienne.first_name
'Adrienne'
>>> getattr(adrienne, ‘first_name’)
‘Adrienne’

This makes sense, because the _name attributes are…attributes. But now for the fun part: did you know that methods of a class are also attributes? For example:

getattr(adrienne, ‘say_hello’) <bound method Person.say_hello of <Person object at 0x10640de50» Now, that doesn’t look very interesting, it didn’t actually do anything. But the power in this is that we can store that return value in a variable, and then call it. Like so:

>>> method = getattr(adrienne, 'say_hello')
>>> method("Someone else")
Hello, Someone else!

Okay, so it’s cool that you can use getattr to get methods as well as properties, but how does that help us? Well, this can definitely be useful in keeping your code DRY if you have some common logic surrounding branching method calls.

For example, if we need to make an API call, but which method we’re using depends on the context, instead of doing this:

if http_method == 'post':    
    requests.post(url, data=data)
elif http_method == 'put':     
    requests.put(url, data=data)

We can do this:

method = getattr(requests, http_method)
method(url, data=data)

The difference may seem a bit trivial in this example, but with objects that have significantly more method options than in this example, the initial method relying on if and elif statements scales particularly poorly. getattr to the rescue!