One of Python’s built-in functions is __getattr__, which turns out to be quite useful but at the same time slightly dangerous. It is especially helpful for creating complex classes without using inheritance. For a brief tutorial, read on.
__getattr__ is a built-in class function just like __init__. It takes two parameters: an object (self, naturally) and a string representing an attribute. It looks like this when it is declared:
def __getattr__(self, attr):
For example, calling an_instance.name is the same as calling an_instance.__getattr__('name'). Remember, the object’s instance is passed as the hidden first parameter; ‘name’ is the attr parameter passed into the method. (There is a related function, getattr, which would do the same thing with the code getattr(an_instance, 'name').)
The Design Pattern
Pythonistas are somewhat particular about how the way things look, even when it doesn’t seem like there is any other way to do something. For instance, suppose you have a class with an attribute that is another class instance, something like parent.child. If you want to access a method of of child, you would typically call parent.child.method(). Pythoneers often consider this ugly, and would much rather prefer to use parent.method() if such a thing were possible. Thanks to the __getattr__ method, it is possible. Here’s how.
class Parent:
def __init__(self, child):
self.child = child
class Child:
def make_statement(self):
print("I am an instance of Child.")
kid = Child()
person = Parent(kid)
If we wanted the child to make a statement, we would have to call person.kid.make_statement(). If we wanted instead to call person.make_statement(), we could modify the Parent class as follows:
class Parent:
def __init__(self, child):
self.child = child
def __getattr__(self, attr):
if hasattr(self.child, attr):
return getattr(self.child, attr)
else:
raise AttributeError(attr)
There are a few things here to explain. First, hasattr works like getattr. The first argument is a class instance and the second is a string. hasattr tests whether or not an instance has attr as an attribute. getattr, as mentioned above, returns the attribute rather than merely testing for its existence.
(It is important to add the else clause when using __getattr__. Leaving it off tends to cause a NoneType error. It is probably good practice anyway, regardless of whether or not it causes an error.)
make_statement() can now be called using
person.make_statement()
This example demonstrates what can be done with __getattr__. The advantage of this Python feature is that classes can have children whose methods and attributes are accessible to the parent. It accomplishes the same thing as inheritance, even though it reverses the terminology. The advantage over inheritance is that some attributes could be blocked. For instance, suppose we modified the above __getattr__ method to be
def __getattr__(self, attr):
if hasattr(self.child, attr) and attr in list_of_valid_child_attrs:
return getattr(self.child, attr)
else:
raise AttributeError(attr)
Now __getattr__ makes sure that the child has the attribute and that the attribute is in a list of attributes it is allowed to access.
The Danger
Python only calls __getattr__ when it is unable to find the attribute declared anywhere. In fact, from what I’ve been able to figure out, the bytecode compiler goes over the code and if a call is made to an unknown attribute, Python determines once and for all what to do based on the __getattr__ method. In other words, __getattr__ is static. Changes during runtime are not reflected.
The real trouble you can get into (and it’s minor trouble) is infinite recursion. Suppose I had mispelled ‘self.child’ in the last example. Instead of calling hasattr(self.child, attr), suppose I called hasattr(self.chil, attr). As Python evaluates the __getattr__ function, it finds a call to an attribute it is not familiar with (namely, ‘chil’). To find out what to do with that it calls __getattr__, where it finds an unknown attribute again, and it repeats ad infinitum.
In this case, the problem is easy to solve (i.e., fix the typo). Other times you might want to do something a bit trickier, and that requires a different solution. Until you encounter that problem, however, have some fun with interrelating classes using children and the __getattr__ method.

