How is the __getattribute__ method used?
It is called before the normal dotted lookup. If it raises AttributeError, then we call __getattr__.
Use of this method is rather rare. There are only two definitions in the standard library:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/" cpython/Lib/_threading_local.py cpython/Lib/importlib/util.py
Best Practice
The proper way to programmatically control access to a single attribute is with property. Class D should be written as follows (with the setter and deleter optionally to replicate apparent intended behavior):
class D(object): def __init__(self): self.test2=21 @property def test(self): return 0. @test.setter def test(self, value): '''dummy function to avoid AttributeError on setting property''' @test.deleter def test(self): '''dummy function to avoid AttributeError on deleting property'''
And usage:
>>> o = D() >>> o.test 0.0 >>> o.test = 'foo' >>> o.test 0.0 >>> del o.test >>> o.test 0.0
A property is a data descriptor, thus it is the first thing looked for in the normal dotted lookup algorithm.
Options for __getattribute__
You several options if you absolutely need to implement lookup for every attribute via __getattribute__.
- raise
AttributeError, causing __getattr__ to be called (if implemented) - return something from it by
- using
super to call the parent (probably object's) implementation - calling
__getattr__ - implementing your own dotted lookup algorithm somehow
For example:
class NoisyAttributes(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self, name): print('getting: ' + name) try: return super(NoisyAttributes, self).__getattribute__(name) except AttributeError: print('oh no, AttributeError caught and reraising') raise def __getattr__(self, name): """Called if __getattribute__ raises AttributeError""" return 'close but no ' + name >>> n = NoisyAttributes() >>> nfoo = n.foo getting: foo oh no, AttributeError caught and reraising >>> nfoo 'close but no foo' >>> n.test getting: test 20
What you originally wanted.
And this example shows how you might do what you originally wanted:
class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name=='test': return 0. else: return super(D, self).__getattribute__(name)
And will behave like this:
>>> o = D() >>> o.test = 'foo' >>> o.test 0.0 >>> del o.test >>> o.test 0.0 >>> del o.test Traceback (most recent call last): File "<pyshell#216>", line 1, in <module> del o.test AttributeError: test
Code review
Your code with comments. You have a dotted lookup on self in __getattribute__. This is why you get a recursion error. You could check if name is "__dict__" and use super to workaround, but that doesn't cover __slots__. I'll leave that as an exercise to the reader.
class D(object): def __init__(self): self.test=20 self.test2=21 def __getattribute__(self,name): if name=='test': return 0. else: # v--- Dotted lookup on self in __getattribute__ return self.__dict__[name] >>> print D().test 0.0 >>> print D().test2 ... RuntimeError: maximum recursion depth exceeded in cmp