[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