Skip to content

Changed to use the super() method #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions _sources/Inheritance/InvokingSuperMethods.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.. Copyright (C) Paul Resnick. Permission is granted to copy, distribute
.. Copyright (C) Paul Resnick and Steve Oney. Permission is granted to copy, distribute
and/or modify this document under the terms of the GNU Free Documentation
License, Version 1.3 or any later version published by the Free Software
Foundation; with Invariant Sections being Forward, Prefaces, and
Expand All @@ -15,7 +15,7 @@ Sometimes the parent class has a useful method, but you just need to execute a l

Say you wanted the ``Dog`` subclass of ``Pet`` to say "Arf! Thanks!" when the ``feed`` method is called, as well as executing the code in the original method.

Here's the original Pet class again.
Here's the original ``Pet`` class again.

.. activecode:: inheritance_pet_class_copy
:nocanvas:
Expand Down Expand Up @@ -62,6 +62,7 @@ Here's the original Pet class again.
self.reduce_boredom()

def feed(self):
print("Called Pet.feed()")
self.reduce_hunger()

def reduce_hunger(self):
Expand All @@ -70,42 +71,42 @@ Here's the original Pet class again.
def reduce_boredom(self):
self.boredom = max(0, self.boredom - self.boredom_decrement)

And here's a subclass that overrides feed() by invoking the the parent class's feed() method; it then also executes an extra line of code. Note the somewhat inelegant way of invoking the parent class' method. We explicitly refer to Pet.feed to get the method/function object. We invoke it with parentheses. However, since we are not invoking the method the normal way, with <obj>.methodname, we have to explicitly pass an instance as the first parameter. In this case, the variable self in Dog.feed() will be bound to an instance of Dog, and so we can just pass self: ``Pet.feed(self)``.
And here's a subclass that overrides ``feed()`` by invoking the the parent class's ``feed()`` method; it then also executes an extra line of code. It does this by calling the built-in function ``super()``. The ``super()`` function returns a special object that allows you to invoke the method of the parent class. So to call the parent class's ``feed()`` method (``Pet.feed()``), we say ``super().feed()``.


.. activecode:: feed_me_example
:nocanvas:
:include: inheritance_pet_class
:include: inheritance_pet_class_copy

from random import randrange

class Dog(Pet):
sounds = ['Woof', 'Ruff']

def feed(self):
Pet.feed(self)
super().feed()
print("Arf! Thanks!")

d1 = Dog("Astro")

d1.feed()

.. note::
Another way to invoke the parent's method is to explicitly refer to the parent class' method and invoke it on the instance. So, in this case, we could say ``Pet.feed(self)``. This is a little more explicit, but it's also a little less flexible. If we later change the name of the parent class, we'd have to change it in all the subclasses. Also, if we later change the class hierarchy, so that ``Dog`` is a subclass of some other class, we'd have to change the code in all the subclasses. So, it's better to use ``super()``.

There's a better way to invoke a superclass's method. Unfortunately, the implementation of python in our ActiveCode windows doesn't support it, so we aren't using it here. In that alternative method, we would call ``super().feed()``. This is nice because it's easier to read, and also because it puts the specification of the class that Dog inherits from in just one place, ``class Dog(Pet)``. Elsewhere, you just refer to ``super()`` and python takes care of looking up that the parent (super) class of Dog is Pet.

This technique is very often used with the ``__init__`` method for a subclass. Suppose that some extra instance variables are defined for the subclass. When you invoke the constructor, you pass all the regular parameters for the parent class, plus the extra ones for the subclass. The subclass' ``__init__`` method then stores the extra parameters in instance variables and calls the parent class' ``__init__`` method to store the common parameters in instance variables and do any other initialization that it normally does.

Let's say we want to create a subclass of ``Pet``, called ``Bird``, and we want it to take an extra parameter, ``chirp_number``, with a default value of 2, and have an extra instance variable, ``self.chirp_number``. Then, we'll use this in the ``hi`` method to make more than one sound.
Let's say we want to create a subclass of ``Pet``, called ``Bird``, and we want it to take an extra parameter, ``chirp_number``, with a default value of ``2``, and have an extra instance variable, ``self.chirp_number``. Then, we'll use this in the ``hi`` method to make more than one sound.

.. activecode:: super_methods_1
:nocanvas:
:include: inheritance_pet_class
:include: inheritance_pet_class_copy

class Bird(Pet):
sounds = ["chirp"]
def __init__(self, name="Kitty", chirp_number=2):
Pet.__init__(self, name) # call the parent class's constructor
# basically, call the SUPER -- the parent version -- of the constructor, with all the parameters that it needs.
super().__init__(name) # call the parent class's constructor
self.chirp_number = chirp_number # now, also assign the new instance variable

def hi(self):
Expand All @@ -120,28 +121,31 @@ Let's say we want to create a subclass of ``Pet``, called ``Bird``, and we want
**Check your understanding**

.. mchoice:: question_inheritance_4
:answer_a: 5
:answer_a: 7
:answer_b: ["Mrrp"]
:answer_c: ["chirp"]
:answer_d: Error
:feedback_a: This would print if the code was print(b1.chirp_number).
:feedback_b: We set b1 to be Bird('tweety', 5) above. Bird is a subclass of Pet, which has ["Mrrp"] for sounds, but Bird has a different value for that class variable. The interpreter looks in the subclass first.
:feedback_a: This would print if the code was print(b2.chirp_number).
:feedback_b: We set b2 to be Bird('Sunny', 7) above. Bird is a subclass of Pet, which has ["Mrrp"] for sounds, but Bird has a different value for that class variable. The interpreter looks in the subclass first.
:feedback_c: The interpeter finds the value in the class variable for the class Bird.
:feedback_d: We ran set b1 to be Bird('tweety', 5) above. Bird has a value set for the attribute sounds.
:feedback_d: We ran set b2 to be Bird('Sunny', 7) above. Bird has a value set for the attribute sounds.
:correct: c

What will print when ``print(b1.sounds)`` is run?
What will the following code print (assuming we use the above definitions of ``Bird`` and ``Pet``)::

b2 = Bird('Sunny', 7)
print(b2.sounds)

.. mchoice:: question_inheritance_5
:answer_a: Error when invoked
:answer_b: The string would not print out but d1 would have its hunger reduced.
:answer_c: The string would print but d1 would not have its hunger reduced.
:answer_b: The string "Arf! Thanks!" would not print out but d1 would still have its hunger reduced.
:answer_c: The string "Arf! Thanks!" would still print out but d1 would not have its hunger reduced.
:answer_d: Nothing would be different. It is the same as the current code.
:feedback_a: Since we are no longer calling the parent method in the subclass method definition, the actions defined in the parent method feed will not happen, and only Arf! Thanks! will be printed.
:feedback_b: Remember that the Python interpreter checks for the existence of feed in the Dog class and looks for feed in Pet only if it isn't found in Dog.
:feedback_c: Since we are no longer calling the parent Pet class's method in the Dog subclass's method definition, the class definition will override the parent method.
:feedback_d: Remember that the Python interpreter checks for the existence of feed in the Dog class and looks for feed in Pet only if it isn't found in Dog.
:correct: c

For the Dog class defined in the earlier activecode window, what would happen when d1.feed() is run if the Pet.feed(self) line was deleted?
For the ``Dog`` class defined in the earlier activecode window, what would happen when ``d1.feed()`` is run if the ``super().feed()`` line was deleted?

4 changes: 2 additions & 2 deletions _sources/Inheritance/TamagotchiRevisited.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ And now we can play the Tamagotchi game with some small changes, such that we ca
return "happy"

def feed(self):
Pet.feed(self)
super().feed()
print("Arf! Thanks!")

class Bird(Pet):
sounds = ["chirp"]
def __init__(self, name="Kitty", chirp_number=2):
Pet.__init__(self, name) # call the parent class's constructor
super().__init__(name) # call the parent class's constructor
# basically, call the SUPER -- the parent version -- of the constructor, with all the parameters that it needs.
self.chirp_number = chirp_number # now, also assign the new instance variable

Expand Down