6

Overview:

class Inner(object):
    def __init__(self, x):
        self.x = x

class Outer(object):
    def __init__(self, z):
        self.inner = Inner(z)

o = Outer(10)

Now, I want the Outer object to behave transparently -- any attributes set on o should be set on o.inner, same for reading: o.something should return, in fact, the value of o.inner.sommething. Kind of like a proxy or a relay.

__getattr__ for Outer seems simple & works:

def __getattr__(self, item):
    return getattr(self, item)

How would I handle __setattr__? I couldn't come up with anything that didn't cause a recursion error and make me hate myself.

Or is the concept itself flawed?

(Another approach I tried was making Outer a subclass of Inner -- this didn't really play aesthetically the present@classmethods, not to mention the IDE would get lost inside those - not able to resolve some attributes. Let's leave that for now, perhaps?)

ntl0ve
  • 1,896
  • 3
  • 19
  • 25
  • Did you mean `self.inner = Inner(z)`? Also, does your getattr method return data from Inner()? The way it is written, it looks like it would just reference Outer. – gfortune Feb 07 '12 at 17:17
  • Yes - it does return. Fixed both, thanks! – ntl0ve Feb 07 '12 at 17:20

1 Answers1

7

The tricky part is setting the inner attribute of the Outer class correctly. What you can do is call the __setattribute__ method of object (base class of Outer):

class Inner(object):
    def __init__(self, x):
        self.x = x

class Outer(object):    
    def __init__(self, z):
        object.__setattr__(self, 'inner', Inner(z))

    def __getattr__(self, name):
        return getattr(self.inner, name)

    def __setattr__(self, name, value):
        return setattr(self.inner, name, value)

Now, it works correctly:

o = Outer(10)
print o.x        # 10
print o.inner.x  # 10
o.g = 3
print o.g        # 3
print o.inner.g  # 3

However, it's not clear to my why you don't want to use inheritance in this case. It seems more natural and pythonic to have Outer inherit from Inner.

jterrace
  • 64,866
  • 22
  • 157
  • 202
  • And the reason print(o.inner) works is because o.inner already exists so it doesn't call `__getattr__` and try to return o.inner.inner, correct? It's not obvious that `__getattr__` does not get called in cases where the requested item already exists as part of the object in question. – gfortune Feb 07 '12 at 17:42
  • @gfortune, yes; for more see [this question](http://stackoverflow.com/questions/3278077/difference-between-getattr-vs-getattribute-in-python). – senderle Feb 07 '12 at 17:51
  • yes, see [\_\_getattr\_\_](http://docs.python.org/reference/datamodel.html#object.__getattr__) docs – jterrace Feb 07 '12 at 18:00