简体   繁体   中英

How to call and override a Parent class method from Child class in Python

In the reproduce method of ResistantVirus class, I'm trying to call reproduce(self, popDensity) of SimpleVirus class, but instead of returning a SimpleVirus object, I want it to return a ResistantVirus object.

Obviously, I can also repeat some code from the SimpleVirus.reproduce method and use the same implementation in my ResistantVirus.reproduce method, but I wonder if it's possible to call and override SimpleVirus.reproduce in order to avoid repetition?

class SimpleVirus(object):

    def __init__(self, maxBirthProb, clearProb):
        self.maxBirthProb = maxBirthProb
        self.clearProb = clearProb

    def reproduce(self, popDensity):
        if random.random() > self.maxBirthProb * (1 - popDensity):
            raise NoChildException('In reproduce()')
        return SimpleVirus(self.getMaxBirthProb(), self.getClearProb())


class ResistantVirus(SimpleVirus):

    def __init__(self, maxBirthProb, clearProb, resistances, mutProb):
        SimpleVirus.__init__(self, maxBirthProb, clearProb)
        self.resistances = resistances
        self.mutProb = mutProb

    def reproduce(self, popDensity)       

      ## returns a new instance of the ResistantVirus class representing the       
      ## offspring of this virus particle. The child should have the same   
      ## maxBirthProb and clearProb values as this virus.

      ## what I sketched out so far and probs has some mistakes:
      for drug in activeDrugs:
          if not self.isResistantTo(drug):
              raise NoChildException
              break

      simple_virus = SimpleVirus.reproduce(self,popDensity)

      return ResistantVirus(simple_virus.getMaxBirthProb(),simple_virus.getClearProb())

It looks like copy could help you out.

import copy

class Parent(object):
    def __init__(self, a):
        self.a = a

    def copy(self):
        return copy.deepcopy(self)

class Child(Parent):
    def __init__(self, a, b):
        super(Child, self).__init__(a)
        self.b = b

The copy method will carry the class of whatever self is (not necessarily Parent). For example:

Child(1,2).copy() # <__main__.Child object at 0x01832E90>

This seems to be your main goal. However, it might also be worth putting some effort into how you structure your tests. I've implemented a different solution using your example code that allows you to inherit tests. Note that the requires you passing your virus a list of keyword arguments ( **kwargs ). Example usage is given after.

import copy, random

class SimpleVirus(object):

    def __init__(self, max_birth_prob, clear_prob):
        self.max_birth_prob = max_birth_prob
        self.clear_prob = clear_prob
        self._tests = [self.simple_test]

    def copy(self):
        return copy.deepcopy(self)

    def simple_test(self, **kwargs):
        return random.random() < self.max_birth_prob * (1 - kwargs['pop_density'])

    def reproduce(self, **kwargs):
        if all(test(**kwargs) for test in self._tests):
            return self.copy()
        raise Exception


class ResistantVirus(SimpleVirus):

    def __init__(self, max_birth_prob, clear_prob, resistances, mut_prob):

        super(ResistantVirus, self).__init__(max_birth_prob, clear_prob)
        self.resistances = resistances
        self.mut_prob = mut_prob
        self._tests.append(self.resistance_test)

    def resistance_test(self, **kwargs):
        return all(drug in self.resistances for drug in kwargs['drug_list'])

The following sometimes reproduces and sometimes raises an Exception .

res_virus = ResistantVirus(0.8, 0.2, ['a', 'b', 'c'], 0.1)
res_virus.reproduce(pop_density=0.3, drug_list=['a', 'b'])

Notice that there is no significant code reuse between the two classes. This is good if you have a strict inheritance chain and things tend to "build-up" as you go. However, if you're going to have a lot of classes all inheriting SimpleVirus , and some of those classes share functionality, it may be worth looking at object composition over inheritance .

Use super to call the parent class' method:

class ResistantVirus(SimpleVirus):
    def __init__(self, maxBirthProb, clearProb, resistances, mutProb):
        self.resistances = resistances
        self.mutProb = mutProb
        super(ResistantVirus, self).__init__(maxBirthProb, clearProb)

    def reproduce(self, popDensity):
        simple_virus = super(ResistantVirus, self).reproduce(popDensity)
        resistances = # TODO
        mutProb = # TODO
        return ResistantVirus(simple_virus.maxBirthProb,
                              simple_virus.clearProb, 
                              resistances, 
                              mutProb)

You can use the type of the instance instead of an explicit type as long as the __init__() signatures are compatible.

class Parent(object):
    def __str__(self):
        return 'I am a Parent'

    def reproduce(self):
        # do stuff common to all subclasses.
        print('parent reproduction')
        # then return an instance of the caller's type
        return type(self)()

class Child(Parent):
    def __str__(self):
        return 'I am a Child'

    def reproduce(self):
        # do stuff specific to Child.
        print('child reproduction')
        # call the parent's method but it will return a
        # Child object
        return super(Child, self).reproduce()


print(Parent().reproduce())

parent reproduction    
I am a Parent

print(Child().reproduce())

child reproduction
parent reproduction
I am a child

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