views:

5988

answers:

4

I want to override access to one variable in a class, but return all others normally. How do I accomplish this with __getattribute__?

I tried the following (which should also illustrate what I'm trying to do) but I get a recursion error:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp
+10  A: 

You get a recurison error because you call the same function, your getattribute. If you use object's __getattribute__ instead, it works:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

This works because object (in this example) is the base class. By calling the base version of __getattribute__ you avoid the recursive hell you were in before.

Ipython output with code in foo.py:

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21
Egil
Interesting. So what are you doing there? Why would object have my variables?
Greg
..and do I want to always use object? What if I inherit from other classes?
Greg
Because it's the parent class. You're talking to the base class's version of __getattribute__() rather than your new shiny (and recursion-addled) __getattribute__()
Oli
Use the class you're inheriting from. Whatever that might be.
Oli
If you look at the first argument to object's function, it is self.Self is the current instance of your class, the object. You use it in a similar manner when you subclass other classes and you need to call their constructur, __init__
Egil
Ah so I'm only taking the function __getattribute__ from the base class, the values are still coming from my class. So really you can use any class up the hierarchy?
Greg
Though I guess object will have a known good __getattribute__?
Greg
Yes, each time you create a class and you don't write your own, you use the __getattribute__ supplied by object.
Egil
Egil: That's not an entirely safe way to look at it. If I'm using a class that inherits another class, that inherits another class that inherits object (and I didn't write any of the inherited classes) If one of those overrides __getattribute__ and you call object's version, it might not work as ...
Oli
... expected. You should always call the next rung up the inheritance ladder (unless you know what you're doing).
Oli
Yes, and in his code, it is indeed object.
Egil
But I see that my comment can be misleading.
Egil
+6  A: 

Are you sure you want to use getattribute? What are you actually trying to achieve?

The easiest way to do what you ask is:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

or:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

Edit: Note that an instance of D would have different values of test in each case. In the first case d.test would be 20, in the second it would be 0. I'll leave it to you to work out why.

Edit2: Greg pointed out that example 2 will fail because the property is read only and the __init__ method tried to set it to 20. A more complete example for that would be:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

Obviously, as a class this is almost entirely useless, but it gives you an idea to move on from.

Singletoned
this really should be the accepted answer. maybe add a short version of Egil's answer to actually answer the original question.
hop
Great thinking outside the box. Much cleaner way to accomplish it.
Egil
Thanks. The correct answer to almost any question is "What are you actually trying to achieve?"
Singletoned
Oh that doesn't quiet work when you run the class though, no? File "Script1.py", line 5, in __init__ self.test = 20AttributeError: can't set attribute
Greg
True. I'll fix that up as a third example. Well spotted.
Singletoned
+3  A: 

Python language reference:

In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.getattribute(self, name).

Meaning:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

You're calling for an attribute called __dict__. Because it's an attribute __getattribute__ get's called in search for __dict__ which calls __getattribute__ which calls ... yada yada yada

return  object.__getattribute__(self, name)

Using the base classes __getattribute__ helps finding the real attribute.

Tim
+4  A: 

Actually, I believe you want to use the __getattr__ special method instead.

Quote from the Python docs:

__getattr__( self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __setattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.

ΤΖΩΤΖΙΟΥ