1

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())
5
  • 2
    shouldn't you be using super() in your __init__ method Commented Mar 21, 2016 at 20:12
  • do not use CamelCase for anything but classes names. Also where are getMaxBirthProb and getClearProb methods? Commented Mar 21, 2016 at 20:13
  • Sorry, I didn't copy paste the rest of the code that has all the getter and setter methods in order to save space here. And there are other methods and things that need to be done so I don't want to do exactly the same as what I did in SimpleVirus.reproduce. Commented Mar 21, 2016 at 20:17
  • Also, I've seen people using super() in their code and I thought it was just another option when calling a parent class method. I've been using Classname.method() in init and otherplaces in my other coding projects and it works fine. Is it bad practice to not use super() and only super() is allowed? Commented Mar 21, 2016 at 20:22
  • No, sometimes, super helps a lot, in case of multiple inheritance, mixins, ... but sometimes calling a parent class method by the parent class name is the only way to achieve what we want of to bypass Python's MRO (Method Resolution Order) implementation. Commented Mar 21, 2016 at 20:25

3 Answers 3

5

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
Sign up to request clarification or add additional context in comments.

4 Comments

thanks! When you say "init are compatible", does that mean they have to have the same attributes? What if child class has a new attribute self.mutProb and I've defined the getter method getMutProb(), can I still do something along the line of "return super(Child,self).reproduce()" to return a child object?
@BoiseDakota It means that __init__ needs to take the same arguments, which is not the case. You can use copy to get around this generally (I've updated my answer below).
@BoiseDakota Yeah, it's not about attributes but about the arguments required to create instances of different classes. If they are different you might (or might not) be in trouble. I edited the answer to clarify that. Will also look at your specific case.
Thank you guys so much! I understand it now. :))
2

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)

Comments

2

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.

4 Comments

Thank you for your answer! I might sound stupid but please bear with me: is there a way to call the parent class method but return child class method with more attributes? I noticed you used "return Child(self.a, self.b)", what if I also want to implement the Parent.method() and other Child.method()? Can I put the returning parent object in a variable and wrap it with a child class object initialization? Sorry again my question is long.
@BoiseDakota So, you'd want syntax like return Child.copy_from_parent(self.b) where the coping over of self.a is done by the parent's implementation of copy?
no. I want to know without implementing a new method copy(). Is it possible to 1.call parent method 2. add more implementations that specific to child class. 2. make the parent object returned into a child object? Thank you! I'm quite confused now please bear with me.
@BoiseDakota Probably? It's still not clear what you're trying to do. If you can come up with a completely minimal example of the structure you want, it will help clarify things.

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.