简体   繁体   中英

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"))

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)

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.

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'.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM