4.4. Getting Object References With getattr

4.4. Getting Object References With getattr

You already know that Python functions are objects. What you don't know is that you can get a reference to a function without knowing its name until run-time, by using the getattr function.

Example 4.10. Introducing getattr

>>> li = ["Larry", "Curly"]
>>> li.pop                       1
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")           2
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe") 3
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")         4
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")           5
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'
1 This gets a reference to the pop method of the list. Note that this is not calling the pop method; that would be li.pop(). This is the method itself.
2 This also returns a reference to the pop method, but this time, the method name is specified as a string argument to the getattr function. getattr is an incredibly useful built-in function that returns any attribute of any object. In this case, the object is a list, and the attribute is the pop method.
3 In case it hasn't sunk in just how incredibly useful this is, try this: the return value of getattr is the method, which you can then call just as if you had said li.append("Moe") directly. But you didn't call the function directly; you specified the function name as a string instead.
4 getattr also works on dictionaries.
5 In theory, getattr would work on tuples, except that tuples have no methods, so getattr will raise an exception no matter what attribute name you give.

4.4.1. getattr with Modules

getattr isn't just for built-in datatypes. It also works on modules.

Example 4.11. The getattr Function in apihelper.py

>>> import odbchelper
>>> odbchelper.buildConnectionString             1
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString") 2
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)                      3
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))                4
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
True
>>> callable(getattr(object, method))            5
True
1 This returns a reference to the buildConnectionString function in the odbchelper module, which you studied in Chapter 2, Your First Python Program. (The hex address you see is specific to my machine; your output will be different.)
2 Using getattr, you can get the same reference to the same function. In general, getattr(object, "attribute") is equivalent to object.attribute. If object is a module, then attribute can be anything defined in the module: a function, class, or global variable.
3 And this is what you actually use in the info function. object is passed into the function as an argument; method is a string which is the name of a method or function.
4 In this case, method is the name of a function, which you can prove by getting its type.
5 Since method is a function, it is callable.

4.4.2. getattr As a Dispatcher

A common usage pattern of getattr is as a dispatcher. For example, if you had a program that could output data in a variety of different formats, you could define separate functions for each output format and use a single dispatch function to call the right one.

For example, let's imagine a program that prints site statistics in HTML, XML, and plain text formats. The choice of output format could be specified on the command line, or stored in a configuration file. A statsout module defines three functions, output_html, output_xml, and output_text. Then the main program defines a single output function, like this:

Example 4.12. Creating a Dispatcher with getattr


import statsout

def output(data, format="text"):                              1
    output_function = getattr(statsout, "output_%s" % format) 2
    return output_function(data)                              3
1 The output function takes one required argument, data, and one optional argument, format. If format is not specified, it defaults to text, and you will end up calling the plain text output function.
2 You concatenate the format argument with "output_" to produce a function name, and then go get that function from the statsout module. This allows you to easily extend the program later to support other output formats, without changing this dispatch function. Just add another function to statsout named, for instance, output_pdf, and pass "pdf" as the format into the output function.
3 Now you can simply call the output function in the same way as any other function. The output_function variable is a reference to the appropriate function from the statsout module.

Did you see the bug in the previous example? This is a very loose coupling of strings and functions, and there is no error checking. What happens if the user passes in a format that doesn't have a corresponding function defined in statsout? Well, getattr will return None, which will be assigned to output_function instead of a valid function, and the next line that attempts to call that function will crash and raise an exception. That's bad.

Luckily, getattr takes an optional third argument, a default value.

Example 4.13. getattr Default Values


import statsout

def output(data, format="text"):
    output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
    return output_function(data) 1
1 This function call is guaranteed to work, because you added a third argument to the call to getattr. The third argument is a default value that is returned if the attribute or method specified by the second argument wasn't found.

As you can see, getattr is quite powerful. It is the heart of introspection, and you'll see even more powerful examples of it in later chapters.