user2757442
user2757442

Reputation: 175

How to prevent instance variables of a parent are overwritten by child class in python?

I have two pretty simple classes written below. Human inherits from the Mammal class. When I call print(jo.drinkMe("sage")), I expect "sick" to be printed, but "fine" is printed. I think that self.problem in the Human class overwrites self.problem in the Mammal class. What do I need to do to my code in order for the print statement at the end to print "sick"?

class Mammal:
    def __init__(self):
        self.problem = "sage"
        self.result = "sick"

    def drinkMe(self,tea):
        if tea == self.problem:
            return self.result
        else:
            return "fine"

class Human(Mammal):
    def __init__(self):
        super().__init__()
        self.problem = "paint"
        self.result = "die"
    def drinkMe(self,tea):
        if tea == self.problem:
            return self.result
        else:
            return super().drinkMe(tea)

jo = Human()
to = Mammal()
print(jo.drinkMe("sage"))

Upvotes: 0

Views: 144

Answers (3)

user2757442
user2757442

Reputation: 175

I found a pretty simple way to prevent the instance variables declared in Human from overriding those in Mammal. The code basically just reassigns the values of self.problem and self.result each time drink_me is called:

class Mammal:

    def __init__(self):
        self.problem = "sage"
        self.result = "sick"

    def drinkMe(self,tea):
        if tea == self.problem:
            return self.result
        else:
            return "fine"
class Human(Mammal):
    def __init__(self):
        super().__init__()
        self.problem = "paint"
        self.result = "die"
    def drinkMe(self,tea):
        result = ''
        if tea == self.problem:
            return self.result
        else:
            super().__init__()
            result = super().drinkMe(tea)
            self.__init__()
            return(result)
jo = Human()
print(jo.drinkMe("sage"))
print(jo.drinkMe("paint"))

This code first prints 'sick' and then 'die'.

Upvotes: 0

calico_
calico_

Reputation: 1221

I hope this helps. It is a little different way of thinking than your original code, but honestly the code you posted was going to require some messy super() calls. I think that what you're aiming for is a subclass of Mammal() with additional options for a result from drinkMe()

What about taking this approach: All Mammals now have a dictionary with drinks and results. Subclasses update this dictionary with their own custom responses to the drink. This way you have a single function that just returns the response for a drink. What should happen if an instance drinks something that does not have a response set? Now you only need to add exception handling once inside drinkMe().

class Mammal:
    def __init__(self):
        self.problems = {'sage': 'sick'}

    def drinkMe(self, tea):
        return self.problems[tea]


class Human(Mammal):
    def __init__(self):
        super().__init__()
        self.problems.update({'paint': 'die'})

jo = Human()
print(jo.drinkMe("sage"))

Edit: To comment more on why your original code printed fine instead of sick.

When you instatiated a Human called jo, jo.problem is set to paint. So, jo.drinkMe calls super.drinkMe which again checks if tea == self.problem. But remember, you still have not changed jo.problem. It is still set to paint, and so fine gets returned.

self is the instance itself, in this case it is jo and when you call the super.drinkMe(), super.__init__() is not run again, so self.problem is still set to whatever it was.

Upvotes: 2

Adam Smith
Adam Smith

Reputation: 54273

The problem you're having is that Mammal only sets its instance attributes problem and result when it's instantiated. jo = Human() instantiates a Mammal object, but then clobbers the problem and result attributes into its own. To be verbose, you have:

class Human(Mammal):
    def __init__(self):
        super().__init__()
        # calls `Mammal.__init__()`, which sets
        # self.problem = "sage"
        # self.result = "sick"
        self.problem = "paint"
        self.result = "die"
        # overwrites what `Mammal.__init__()` did a
        # couple lines ago.
   ...

You might instead do:

class Mammal(object):
    problem = "sage"
    result = "sick"

    def drink_me(self, tea):
        if tea == self.problem:
            return self.result
        else:
            return "fine"


class Human(Mammal):
    problem = "paint"
    result = die

    def drink_me(self, tea):
        if tea == self.problem:
            return self.result
        else:
            return super().drink_me(tea)

Upvotes: 2

Related Questions