What is getattr() exactly and how do I use it?
Objects in Python can have attributes -- data attributes and functions to work with those (methods). Actually, every object has built-in attributes (try dir(None)
, dir(True)
, dir(...)
, dir(dir)
in Python console).
For example you have an object person
, that has several attributes: name
, gender
, etc.
You access these attributes (be it methods or data objects) usually writing: person.name
, person.gender
, person.the_method()
, etc.
But what if you don't know the attribute's name at the time you write the program? For example you have attribute's name stored in a variable called attr_name
.
if
attr_name = 'gender'
then, instead of writing
gender = person.gender
you can write
gender = getattr(person, attr_name)
Some practice:
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
>>> class Person():
... name = 'Victor'
... def say(self, what):
... print(self.name, what)
...
>>> getattr(Person, 'name')
'Victor'
>>> attr_name = 'name'
>>> person = Person()
>>> getattr(person, attr_name)
'Victor'
>>> getattr(person, 'say')('Hello')
Victor Hello
getattr
will raise AttributeError
if attribute with the given name does not exist in the object:
>>> getattr(person, 'age')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
But you can pass a default value as the third argument, which will be returned if such attribute does not exist:
>>> getattr(person, 'age', 0)
0
You can use getattr
along with dir
to iterate over all attribute names and get their values:
>>> dir(1000)
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> obj = 1000
>>> for attr_name in dir(obj):
... attr_value = getattr(obj, attr_name)
... print(attr_name, attr_value, callable(attr_value))
...
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True
...
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True
...
>>> getattr(1000, 'bit_length')()
10
A practical use for this would be to find all methods whose names start with test
and call them.
Similar to getattr
there is setattr
which allows you to set an attribute of an object having its name:
>>> setattr(person, 'name', 'Andrew')
>>> person.name # accessing instance attribute
'Andrew'
>>> Person.name # accessing class attribute
'Victor'
>>>
getattr(object, 'x')
is completely equivalent to object.x
.
There are only two cases where getattr
can be useful.
- you can't write
object.x
, because you don't know in advance which attribute you want (it comes from a string). Very useful for meta-programming. - you want to provide a default value.
object.y
will raise anAttributeError
if there's noy
. Butgetattr(object, 'y', 5)
will return5
.
For me, getattr
is easiest to explain this way:
It allows you to call methods based on the contents of a string instead of typing the method name.
For example, you cannot do this:
obj = MyObject()
for x in ['foo', 'bar']:
obj.x()
because x is not of the type builtin
, but str
. However, you CAN do this:
obj = MyObject()
for x in ['foo', 'bar']:
getattr(obj, x)()
It allows you to dynamically connect with objects based on your input. I've found it useful when dealing with custom objects and modules.
A pretty common use case for getattr
is mapping data to functions.
For instance, in a web framework like Django or Pylons, getattr
makes it straightforward to map a web request's URL to the function that's going to handle it. If you look under the hood of Pylons's routing, for instance, you'll see that (by default, at least) it chops up a request's URL, like:
http://www.example.com/customers/list
into "customers" and "list". Then it searches for a controller class named CustomerController
. Assuming it finds the class, it creates an instance of the class and then uses getattr
to get its list
method. It then calls that method, passing it the request as an argument.
Once you grasp this idea, it becomes really easy to extend the functionality of a web application: just add new methods to the controller classes, and then create links in your pages that use the appropriate URLs for those methods. All of this is made possible by getattr
.