"
This article is part of in the series
Published: Wednesday 27th February 2013
Last Updated: Wednesday 29th December 2021

Once we know how to check if an object has an attribute in Python, the next step is to get that attribute. In Python, besides the normal dot-style attribute access, there's a built-in function, getattr, which is also very useful for accessing an attribute.

Python's Getattr

The built-in function getattr(object, name[, default]) returns the value of a named attribute of object, where name must be a string. If object has an attribute with name, then the value of that attribute is returned. On the other hand, if object does not have an attribute with name, then the value of default is returned or AttributeError is raised if default is not provided.

[python]
>>> t = ('This', 'is', 'a', 'tuple')
>>> t.index('is')
1
>>> getattr(t, 'index')
<built-in method index of tuple object at 0x10c15e680>
>>> getattr(t, 'index')('is')
1
[/python]

In the previous code snippet, we created a tuple t and called its method index in the normal style and then in a getattr style. Notice getattr(t, 'index') returns a function that is bound to the object t.

What happens if an object does not have an attribute with name? Recall the definition of getattr, which states that either the value of default will be returned or an AttributeError will be raised.

[python]
>>> getattr(t, 'len')
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'tuple' object has no attribute 'len'
>>> getattr(t, 'len', t.count)
<built-in method count of tuple object at 0x10c15e680>
[/python]

Besides "normal" objects like tuples, lists, and class instances, getattr also accepts modules as arguments. Since modules are also objects in Python, the attributes of modules can be retrieved just like any attribute in an object.

[python]
>>> import uuid
>>> getattr(uuid, 'UUID')
<class 'uuid.UUID'>
>>> type(getattr(uuid, 'UUID'))
<type 'type'>
>>> isinstance(getattr(uuid, 'UUID'), type)
True
>>> callable(getattr(uuid, 'UUID'))
True
>>> getattr(uuid, 'UUID')('12345678123456781234567812345678')
UUID('12345678-1234-5678-1234-567812345678')
[/python]

getattr and the Dispatcher Pattern

Compared to the usual way to access an attribute, getattr is more volatile since it accepts a string which identifies the name of the attribute, instead of hardcoding the name in source code. Therefore, it's natural to use getattr as a dispatcher. For example, if you want to write a program that transforms different types of files in different ways depending on their extensions, you could write the transformation logic as separate functions in a class and call them based on the current file extension.

[python]
>>> from collections import namedtuple
>>> import os
>>> Transformer = namedtuple('Transformer', ['transform_png', 'transform_txt'])
>>> transformer = Transformer('transform .png files', 'transform .txt files')
>>> # Suppose the current directory has two files 'tmp.png' and 'tmp.txt'
>>> current_directory = os.path.dirname(os.path.abspath(__file__))
>>> for dirname, dirnames, filenames in os.walk(current_directory):
... for filename in filenames:
... path = os.path.join(dirname, filename)
... filename_prefix, ext = filename.rsplit('.', 1)
... if ext == 'png' or ext == 'txt':
... print(getattr(transformer, 'transform_{0}'.format(ext)))
...
transform .png files
transform .txt files
[/python]

Tips and Suggestions for using getattr

  • Although getattr is quite handy when dealing with introspection and meta-programming, it does make the code harder to read. So, when you are itching to use getattr, calm down first to think whether it's really necessary. More often than not, your use case probably does not require getattr.
  • When you do want to use getattr, write the code slower than you usually do and make sure you have unit tests to back it up, because almost all bugs involving a getatrr are runtime errors and hard to fix. It's much more efficient to save time by writing the code correctly in one pass than debugging it later.

About The Author

Xiaonuo Gantan