There are three methods that are often confusing to the beginners: getattr()
, __getattr__
, and __getattribute__
. This article will try to clear the confusion between these three methods.
getattr()
First of all, getattr()
is a built-in function in Python. It can be evoked from anywhere in your code. When called, it returns an attribute of an object. For example:
class User:
SPECIES = 'human'
def __init__(self, name):
self.name = name
def greet(self):
print(f'Hi, my name is {self.name}')
# Let's first create an instance
john = User('John')
# Now we can use getattr to dynamically find
# class attributes
getattr(john, 'SPECIES') # -> 'human'
# We can also fetch methods (methods are attributes too)
greet = getattr(john, 'greet') # -> returns a method
greet() # now we can call the method
You may wonder why we haven’t used john.SPECIES
or john.greet()
but sometimes you need a way to dynamically call an attribute of an object, because you don’t know in advance which method will be called.
__getattr__
vs __getattribute__
These two methods are implemented as methods of a class. __getattribute__
is always called. Essentially this method is used to find an attribute of a class. If it fails, it will raise an AttributeError
. In case it fails, and class implements __getattr__
, then __getattr__
is called right after. Therefore, the biggest difference is that __getattr__
is called for attributes that don’t actually exist on a class.
This is the explanation from the documentation:
object.__getattribute__(self, name)
: Called unconditionally to implement attribute accesses for instances of the class. If the class also defines__getattr__()
, the latter will not be called unless__getattribute__()
either calls it explicitly or raises an AttributeError.
If you are looking to implement attribute that is dynamically generated for the class __getattr__
is the method you are looking for. In any case __getattr__
is most probably the thing that you’re looking for, and the reason why you ended up reading this article.
I often find __getattr__
useful when writing wrapper around Python logging:
# logconf.py
import logging
import logging.config
from logging import Logger
class LoggerWrapper:
def __init__(self):
# Put your logging config here
logging.config.dictConfig({
'version': 1,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'default': {
'handlers': ['console'],
'level': 'DEBUG',
}
}
})
self.logger = logging.getLogger('default')
def __getattr__(self, name):
"""
if you call ``debug`` on the instance of this class then
__getattr__ finds ``debug`` and passes it to self.logger instance
"""
return getattr(self.logger, name)
logger = LoggerWrapper()
Now, if you import the logger wrapper, you can call all Python logging methods on it, without ever explicitly defined them in your wrapper:
>>> from logconf import logger
>>> logger.info('This is info message')
This is info message
>>> logger.error('This is error message')
This is error message
For example, the info()
method is not defined on LoggerWrapper
. Therefore, the __getattr__
is called. The logger.info
is “transformed” into __getattr__(self, 'info')
and it is passed down to getattr(self.logger, 'info')
. This returns the corresponding method from the Python logger class, and now this method is called with the original 'This is info message'
string.