Previous Lecture Lecture 6 Next Lecture

Lecture 6, Thu 01/25

Pytest, Inheritance

Slides folder

Recorded video

Plan for today

Inheritance

For pytest:


# Animal.py
class Animal:
	''' Animal class type that contains attributes for all animals '''

    def __init__(self, species=None, name=None):
        self.species = species
        self.name = name

    def setName(self, name):
        self.name = name

    def setSpecies(self, species):
        self.species = species

    def info(self):
        return f"Species: {self.species}\nName: {self.name}"

    def getSound(self):
        return "I'm an Animal!!!"

if __name__ == "__main__":
    pet = Animal()
	rarebird = Animal("phoenix", "Zarra")
	print(rarebird.info())
	print(rarebird.getSound()) # I’m an Animal!!!

Testing the class and its methods

Once we have a class and its methods, we can generate a test file by saving a copy of the class definition and changing the names of the class and the methods (as is shown in lab02 instructions):

from Animal import Animal

class TestAnimal:
    
    def test__init__default(self):
        a1 = Animal()
        assert a1.species == None
        assert a1.name == None
    
    def test__init__initialized(self):
        a1 = Animal("bird", "Gosha")
        assert a1.species == "bird"
        assert a1.name == "Gosha"  

    def test_setSpecies(self):
        pass

    def test_setName(self):
        pass

    def test_getSpecies(self):
        a1 = Animal("horse", "Lala")
        assert a1.getName() == "Lala"

    def test_getName(self):
        pass

    def test_info(self):
        pass

    def test_getSound(self):
        pass
                

Creating a derived class

# Cow.py

from Animal import Animal

class Cow(Animal):
    # Available method for the Cow Class 
    def setSound(self, sound):
        self.sound = sound

and instantiate a specific cow:

mycow = Cow("cow", "Betsy")
print(mycow.info())
mycow.setSound("Moo") # Sets a Cow sound attribute to "Moo"
print(mycow.getSound()) # I’m an Animal!!! (calls the `Animal.getSound` method)
# in Cow class
def getSound(self):
	return f"{self.sound}!"
mycow = Cow("cow", "Betsy")
mycow.setSound("Moo") # Sets a Cow sound to "Moo"
print(mycow.getSound()) # Moo!
rarebird = Animal("phoenix", "Zarra")
print(rarebird.info())
print(rarebird.getSound()) # I’m an Animal!!!

Note: The constructed object type will dictate which method in which class is called.

We can build on the parent class methods and further extend them in the derived class. For example, if we wanted the cow information to include the sound string as well, we can re-use the existing info like so:

# in Cow class
def info(self):
    """ Make the default output look like:
        ---
        Species: None
        Name: None
        Sound: None
    """
       return f"---\n{super().info()}\nSound: {self.sound}"

Troubleshooting

If you get a TypeError: object.__init__() takes exactly one argument (the instance to initialize) and your __init__() constructor looks correct, double-check the indentation of your code: everything that belongs to the class definition needs to be indented underneath the class keyword.