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 name
d 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 usegetattr
, calm down first to think whether it's really necessary. More often than not, your use case probably does not requiregetattr
. - 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 agetatrr
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.