[Tutor] __getattribute__

Steven D'Aprano steve at pearwood.info
Sat Feb 4 03:36:24 CET 2012


Stayvoid wrote:
> Hi!
> 
> Could you provide some examples (easy and hard ones) and comments on the topic?
> I'm trying to understand how this thing works.

When you do an attribute lookup on an object, say obj.attr, Python uses 
something like a search path to find the attribute: it tries various things, 
and looks in various places, until it has a success or finally gives up. 
Here's a simplified version:

1) First try to create a computed attribute using the special method
    __getattribute__ (new style classes only).
2) If not successful, look in the instance itself.
3) If not found, look in the class.
4) If still not found, look in any superclasses (if any).
5) If still not found, try to create a computed attribute using the
    special __getattr__ method.
6) If not successful, raise AttributeError.

"New style classes" are those that inherit from object, or a Python built-in 
type like str, int, list, etc.

class Spam:  # "classic class", __getattribute__ is ignored
     pass

class Ham(object):  # "new style" class, __getattribute__ is special
     pass

"Classic classes" come from the earliest versions of Python. "New style 
classes" started in Python 2.2, which is not so new any more, but the name has 
stuck. Starting from Python 3, all classes are "new style" and the distinction 
can be ignored.


Notice that there are two special methods: __getattribute__ is always called, 
and if it returns a value, that value is used. __getattr__ is only called if 
everything else fails. Here's an example of how you might use them:

class Test(object):
     a = "this is attached to the class"
     def __init__(self):
         self.b = "this is attached to the instance"
     def __getattr__(self, name):
         print("calling __getattr__")
         if name == 'c':
             return "this is computed by __getattr__"
         else:
             # Nothing left to do. You have to raise an exception yourself.
             raise AttributeError('no such attribute')
     def __getattribute__(self, name):
         print("calling __getattribute__")
         if name == 'd':
             return "this is computed by __getattribute__"
         else:
             # Always let the superclass try.
             return super(Test, self).__getattribute__(name)



To test it, copy and paste the class definition into IDLE or the interactive 
interpreter, and then experiment. E.g.:


py> instance = Test()
py> instance.d
calling __getattribute__
'this is computed by __getattribute__'



What's the difference between class attribute a and instance attribute b? 
Class attributes are shared across all instances, while instances each get 
their own personal version of instance attributes. In the example above, where 
self.b gets assigned the same value every time, the difference is 
insignificant, but normally you might do something like this:


class PrintJob(object):
     size = "A4"  # Set the global default
     def __init__(self, data, size=None):
         if size is not None:
             self.size = size  # override the default
         self.data = data


Now each PrintJob gets its own size, but only when needed; otherwise the 
default A4 gets used instead.


Last but not least... __getattribute__ and __getattr__ are used for looking up 
attributes. You can also write attributes, and delete them, and Python 
provides magic methods to handle them too:

__setattr__ -- used to write the attribute, it is ALWAYS called if present
__delattr__ -- used to delete the attribute, it is ALWAYS called if present


Be warned that using __setattr__ is tricky to get right, and __delattr__ is 
hardly ever needed (at least in my experience). Actually all of these magic 
attr methods are hardly ever needed, but __delattr__ is even less common than 
the rest.

Abuse of magic attr methods can lead to hard to understand code and 
mysterious, hard-to-solve bugs. Consider them for advanced use only.



-- 
Steven


More information about the Tutor mailing list