How to call a data member of the base class if it is being overwritten as a property in the derived class?
Asked Answered
G

5

5

This question is similar to this other one, with the difference that the data member in the base class is not wrapped by the descriptor protocol.

In other words, how can I access a member of the base class if I am overriding its name with a property in the derived class?

class Base(object):
    def __init__(self):
        self.foo = 5

class Derived(Base):
    def __init__(self):
        Base.__init__(self)

    @property
    def foo(self):
        return 1 + self.foo # doesn't work of course!

    @foo.setter
    def foo(self, f):
        self._foo = f

bar = Base()
print bar.foo

foobar = Derived()
print foobar.foo

Please note that I also need to define a setter because otherwise the assignment of self.foo in the base class doesn't work.

All in all the descriptor protocol doesn't seem to interact well with inheritance...

Grade answered 29/6, 2009 at 10:3 Comment(0)
C
3

Defining

def __init__(self):
    self.foo = 5

in Base makes foo a member (attribute) of the instance, not of the class. The class Base has no knowledge of foo, so there is no way to access it by something like a super() call.

This is not necessary, however. When you instanciate

foobar = Derived()

and the __init__() method of the base class calls

self.foo = 5

this will not result in the creation / overwriting of the attribute, but instead in Derived's setter being called, meaning

self.foo.fset(5)

and thus self._foo = 5. So if you put

return 1 + self._foo

in your getter, you pretty much get what you want. If you need the value that self.foo is set to in Base's constructor, just look at _foo, which was set correctly by the @foo.setter.

Coprophagous answered 29/6, 2009 at 10:24 Comment(1)
All of the answers are very interesting and I upvoted most of them but this one was straight to the point, it really made understand me the problem at hand.Grade
O
9

Life is simpler if you use delegation instead of inheritance. This is Python. You aren't obligated to inherit from Base.

class LooksLikeDerived( object ):
    def __init__( self ):
        self.base= Base()

    @property
    def foo(self):
        return 1 + self.base.foo # always works

    @foo.setter
    def foo(self, f):
        self.base.foo = f

But what about other methods of Base? You duplicate the names in LooksLikeDerived and simply.

def someMethodOfBase( self, *args, **kw ):
    return self.base.someMethodOfBase( *args **kw )

Yes, it doesn't feel "DRY". However, it prevents a lot of problems when "wrapping" some class in new functionality like you're trying to do.

Orchidaceous answered 29/6, 2009 at 10:41 Comment(2)
Yeah, I'm indeed looking for more "pythonic" ways to organize my code, and I have been questioning my inheritance-based approach for a while. Your alternative, though, is not that convincing in my case, as I have a several levels deep inheritance chain, where each level defines new features. Having to redefine all the methods at each level would be definitely unfeasible. But I see your point.Grade
@UncleZeiv: This is not an EITHER-OR situation. You have many, many design patterns, including Strategy, Composition, Delegation and Inheritance. Focus on Inheritance is a bad thing.Orchidaceous
C
3

Defining

def __init__(self):
    self.foo = 5

in Base makes foo a member (attribute) of the instance, not of the class. The class Base has no knowledge of foo, so there is no way to access it by something like a super() call.

This is not necessary, however. When you instanciate

foobar = Derived()

and the __init__() method of the base class calls

self.foo = 5

this will not result in the creation / overwriting of the attribute, but instead in Derived's setter being called, meaning

self.foo.fset(5)

and thus self._foo = 5. So if you put

return 1 + self._foo

in your getter, you pretty much get what you want. If you need the value that self.foo is set to in Base's constructor, just look at _foo, which was set correctly by the @foo.setter.

Coprophagous answered 29/6, 2009 at 10:24 Comment(1)
All of the answers are very interesting and I upvoted most of them but this one was straight to the point, it really made understand me the problem at hand.Grade
C
1
class Foo(object):
    def __new__(cls, *args, **kw):
        return object.__new__(cls, *args, **kw)

    def __init__(self):
        self.foo = 5

class Bar(Foo):
    def __new__(cls, *args, **kw):
        self = object.__new__(cls, *args, **kw)
        self.__foo = Foo.__new__(Foo)
        return self

    def __init__(self):
        Foo.__init__(self)

    @property
    def foo(self):
        return 1 + self.__foo.foo

    @foo.setter
    def foo(self, foo):
        self.__foo.foo = foo

bar = Bar()
bar.foo = 10
print bar.foo
Comeuppance answered 29/6, 2009 at 13:13 Comment(0)
P
0

once you have property with same name 'foo' it overrides the behaviour of access of name 'foo' only way out seems that you explicitly set 'foo' in dict

btw: I use python 2.5 hence had to change code a bit

class Base(object):
    def __init__(self):
        self.foo = 5

class Derived(Base):
    def __init__(self):
        Base.__init__(self)

    def g_foo(self):
        return 1 + self.__dict__['foo'] # works now!

    def s_foo(self, f):
        self.__dict__['foo'] = f
        self._foo = f

    foo = property(g_foo, s_foo)

bar = Base()
print bar.foo

foobar = Derived()
print foobar.foo
Principate answered 29/6, 2009 at 10:25 Comment(0)
I
0

Honestly, the thing to look at here is that you're trying to twist your code around a design that is simply poor. The property descriptors handle the request for a 'foo' attribute, and you want to bypass these completely, which is just wrong. You're already causing Base.init to assign foobar._foo = 5, so thats exactly where the getter needs to look, too.

class Base(object): def init(self): self.foo = 5

class Derived(Base):
  def __init__(self):
    Base.__init__(self)

  @property
  def foo(self):
    return 1 + self._foo # DOES work of course!

  @foo.setter
  def foo(self, f):
    self._foo = f

bar = Base()
print bar.foo

foobar = Derived()
print foobar.foo
Idiosyncrasy answered 29/6, 2009 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.