0

I have the following code:

class Desc(object):
    @property
    def color_desc(self):
        return 'Color is ' + self.color

    @property
    def brand_desc(self):
        return 'Brand is ' + self.brand

class Text(Desc):
    def __init__(self):
        self.color = 'blue'
        self.brand = 'volvo'

def main():
    t = Text()
    print t.color_desc

if __name__ == '__main__':
    main()

This correctly works and outputs Color is blue when run. However, if I modify the code slightly so that the property is set to an actual Class attribute, as in:

class Desc(object):
    def __init__(self):
        self._color_desc = color_desc
        self._brand_desc = brand_desc

    @property
    def color_desc(self):
        return self._color_desc

    @color_desc.setter
    def color_desc(self):
        self._color_desc = 'Color is ' + self.color

    @property
    def brand_desc(self):
        return self._brand_desc

    @property
    def brand_desc(self):
        self._brand_desc = 'Brand is ' + self.brand


class Text(Desc):
    def __init__(self):
        self.color = 'blue'
        self.brand = 'volvo'

All of a sudden, it errs out with AttributeError: 'Text' object has no attribute '_color_desc'. How come the Text attributes are inherited correctly in the first place but cannot be accessed in the second one. To me, these two solutions seem to do the same thing.

1
  • just invoke base class's init method from child class's init method. Commented Mar 21, 2016 at 13:13

3 Answers 3

3

You need to call the __init__ of the parent class.

Do something like this:

class Text(Desc):
    def __init__(self):
        self.color = 'blue'
        self.brand = 'volvo'
        super(Text, self).__init__(0,0)

The init function of your Desc class is incorrectly define. Do this:

class Desc(object):
    def __init__(self, color_desc, brand_desc):
        self._color_desc = color_desc
        self._brand_desc = brand_desc
Sign up to request clarification or add additional context in comments.

7 Comments

super(Text, self).__init__() should be at the bottom.
I see, so the constructor is overridden. However, adding the super statement throws this error: NameError: global name 'color_desc' is not defined.
Because it isn't. What value is Desc.__init__ supposed to use to set self._color_desc?
@chepner The value set by the setter of each property.
But you're not using the setter; you're assigning directly to the underlying attribute. The setter is only invoked if you assign to the name of the property.
|
3

In your Text class, the __init__ is overriding the Desc.__init__ class, so the _color_desc attributes is not initialized.

Comments

-1

It's not clear why you thought your second snippet would work at all. It has the following obvious issues:

  • Desc.__init__ is using two variables (color_desc and brand_desc) that aren't actually passed to it - are you expecting them to be in the global scope?
  • You have two (different!) getters, one of which returns something, and no setter for brand_desc.
  • The color_desc setter neither takes nor uses any parameter, so what exactly is it supposed to be setting?
  • The subclass Text.__init__ doesn't call or pass anything to the superclass version, or do the job itself.

I think what you intended was something like:

class Desc(object):

    def __init__(self, color_desc, brand_desc):
        self._color_desc = color_desc
        self._brand_desc = brand_desc

    @property
    def color_desc(self):
        return 'Color is ' + self._color_desc

    @color_desc.setter
    def color_desc(self, new_color):
        self._color_desc = new_color

    @property
    def brand_desc(self):
        return 'Brand is ' + self._brand_desc

    @brand_desc.setter
    def brand_desc(self, new_brand):
        self._brand_desc = new_brand    


class Text(Desc):

    def __init__(self):
        super(Text, self).__init__('blue', 'volvo')

Although now it looks like Text should be an instance, not a subclass, of Desc. You could also simplify by not assigning to e.g. color_desc at all, using a read-only property:

class Desc(object):

    def __init__(self, color, brand):
        self.color = color
        self.brand = brand

    @property
    def color_desc(self):
        return 'Color is ' + self.color

    @property
    def brand_desc(self):
        return 'Brand is ' + self.brand


car = Desc('blue', 'volvo')
car.color = 'red'
print car.color_desc  # Color is red

Now you set the instance's .color directly, but .color_desc still gives you the nicely-formatted version.

2 Comments

Yes, the intended code was as you said. I hurried the example code and missed a thing or two. Anyway, the simplified solution won't do since I specifically need a Car that has-a Description. The code was just an example and in reality the Description class should be reused for e.g. Boats, Trains, etc.
@mart1n if you need "a Car that has-a Description" then I think you're looking for composition, not inheritance (the latter would imply that a Car is-a Description, which doesn't seem right).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.