Python's getattr function can dynamically lookup an attribute on any object by its name.
Let's talk about this slightly advanced Python built-in function.
Let's say we have an object and the name of an attribute on that object, represented as a string:
>>> from dataclasses import dataclass
>>> @dataclass
>>> class Point:
... x: float
... y: float
... z: float
...
>>> point = Point(1, 2, 3)
>>> attribute = input("Enter x, y, or z: ")
Enter x, y, or z: x
>>> attribute
'x'
How can we access the x attribute on that point object, given that we don't know the attribute name we need as we're writing our code?
We can use Python's built-in getattr function for this:
>>> getattr(point, attribute)
1
The getattr function accepts an object and an attribute name and it returns the value of that attribute.
If the attribute isn't defined, an exception will be raised:
>>> attribute = "a"
>>> getattr(point, attribute)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'a'
Though we can also supply an optional default value to return if that attribute doesn't exist:
>>> getattr(point, "a", 0)
0
That default value will only be used if the attribute doesn't exist:
>>> getattr(point, "x", 0)
1
What if just want to check whether an object has a certain attribute?
For example, let's say we'd like to know whether an object has a __len__ method (implying that it would work with the built-in len function).
We could try to access that attribute and set a boolean to either True or False based on whether an AttributeError is raised:
def has_length(obj):
"""Return True if the object works with len()."""
try:
obj.__len__
except AttributeError:
return False
return True
Or we could use the built-in hasattr function to specifically check whether the object has a __len__ attribute:
def has_length(obj):
"""Return True if the object works with len()."""
return hasattr(obj, "__len__")
The hasattr function does access the attribute under the hood, but it only returns True or False based on whether an AttributeError exception was raised.
__dict__ directly?If you're familiar with how Python stores its attributes, you might be wondering why getattr exists.
Can't we directly access the __dict__ dictionary on our object instead?
>>> point.__dict__
{'x': 1, 'y': 2, 'z': 3}
We can!
Directly accessing __dict__ dictionary works:
>>> attribute = "x"
>>> point.__dict__[attribute]
1
But it only works sometimes.
For example, here's a class with an area property:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
Each instance of this class has an area attribute:
>>> rect = Rectangle(3, 4)
>>> rect.area
12
But that attribute doesn't exist in the object's __dict__ dictionary:
>>> rect.__dict__["area"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'area'
But the built-in getattr function will access that attribute as expected:
>>> getattr(rect, "area")
12
So __dict__ doesn't work for properties.
It also doesn't work for classes that use __slots__ to store their attributes instead of __dict__.
__getattribute__ method?The built-in getattr function relies on an object's __getattribute__ method.
>>> rect.__getattribute__
<method-wrapper '__getattribute__' of Rectangle object at 0x76850e673cb0>
Instead of using getattr, can't we call the __getattribute__ method directly?
>>> rect.__getattribute__("area")
12
This does work, but we're not supposed to call this method directly.
Dunder methods are a contract between the implementer of an object and the Python interpreter. These methods are meant to be defined, but not called.
It's Python's job to call dunder methods, not ours.
So we should use the getattr function for the same reason we use the len function instead of calling the __len__ method and the str function instead of calling the __str__ method.
getattr if you can avoid itIf you ever see a hard-coded string being accessed with the getattr function, this is a sign that you probably don't need to use getattr:
>>> getattr(point, "x")
1
If you know the attribute you need to access at the time you write your code, just access that attribute!
>>> point.x
1
If there's a possibility that the attribute you're accessing might not exist you could use getattr with a default value, but that's a pretty unusual scenario in Python:
>>> getattr(point, "a", 0)
0
Usually we know whether each object we're working is expected to have certain attributes.
getattr for dynamic attribute lookupsThe next time you need to dynamically look up an attribute based on a string that represents an attribute name, use Python's built-in getattr function:
some_value = getattr(some_object, some_attribute)
If you need to dynamically control the value of an attribute while it's being looked up on your class's instances, look into defining a __getattr__ method or a __getattribute__ method.
We don't learn by reading or watching. We learn by doing. That means writing Python code.
Practice this topic by working on these related Python exercises.
__repr__ methods
snake_case: Utilities to allow "snake_case" usage in a class with "camelCase" methods
FieldTracker: Class that tracks which attributes have changed
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.