Introducing the super() Function
00:00 Sometimes you want to override a certain method, but maybe you just want to add a bit to the end or to the beginning. You don’t want to just override the whole thing, especially if the method is quite large.
00:12
The super()
function allows you to access the original method of the parent class while you’re still overriding it in your subclass. So to put that in a slightly different way, sometimes you need to extend a certain method, and within that method, you can call the parent class’s method with the super()
function.
00:32
This is very common to see in an .__init__()
constructor. For example, you’ll recognize most of this code from previous lessons.
00:43
You’ve got your custom exception up here. You’ve got your Point
class, which has been abbreviated here, just so it all fits on the screen. You’ve got your Shape
class, which has a space here for something that we’re going to add in in a second.
00:57
Then you’ve got your Square
class, which subclasses Shape
and adds in this check here to make sure that it has four points. But this code currently has a problem.
01:09
You accept a list of points as the attribute that goes to self.points
—same here—and you’re checking if the length is 4
. There should be four points for a square. That makes sense.
01:22 But someone could pass in a list of numbers or strings, and as long as there are four of them, that would still work. So you really want to add in a check here to make sure each of them are points.
01:33
You might do this by saying for point in points:
if not isinstance(point, Point)
, then you can raise a TypeError
. We’re not subclassing this because the TypeError
is quite a common type of error.
01:47
You often pass in the wrong type to a function, and in this case, that’s what’s happening here. If all the points are not instances of the Point
class, then they should raise a TypeError
.
02:00 But now, the way you’ve currently got it set up, you’d have to copy this check into here before or after.
02:10
That’s not ideal because then we’re just copying code, which is one of the big reasons we learn how to code is to not repeat ourselves. So what you can do is use the super()
function.
02:21 You’ll see that this line here has now changed.
02:25
Instead of self.points = points
, you’re calling super().__init__(points)
. What’s happening here? super()
is like a reference to the parent, and then it’s saying, okay, get the parent, and then get the .__init__()
method from the parent here, and then we’ll pass in the points to that function.
02:53 Basically, this is like just adding this check before this check.
03:00
You could put the super()
beforehand. That would also work. But it makes sense to have it after because we want to make sure that we’ve gone through all the checks before we assign the points to the instance as an attribute, namely that the code has checked whether the number of points is 4
and then whether all the points are actually instances of the Point
class, and finally assign the points to the instance.
03:26
Let’s look at that in action. With this code loaded up, you can see that the Square
shape has the super()
call in it, but if we were to revert this and put this back into self.points
, so it’s not using the super()
function, this is no longer paying attention to the check to make sure all the points are actually instances of the Point
class.
03:52
You can verify this by maybe creating an instance of Square
, and instead of passing four points, we’ll just pass in four ones, four integers.
04:02 There are four of them, but they’re not points. So this shouldn’t really be a valid shape. Control + S to save it, and then F5 to run it.
04:11
You’ll see that it creates without any errors, and if you look at box
, you’ll see that it’s a Square
object, but if you look at the points, then you’ll see that this is just a list of numbers, not of points.
04:24
So we really want to catch this. The naive way might be to just copy and paste all of this into here. Now this won’t run. If I press Control + S and then F5 to run it, I’ll get an error. It’s saying all points should be members of the Point
class, so that works. But we’re duplicating code here, and we don’t need to.
04:44
What we can do instead is add our check and then call this constructor method as if it was part of this method. And the way you can do that is with the super()
function.
04:59
So super()
, that grabs the original class, then we reference the .__init__()
constructor method, then we pass in the arguments that would be passed into that.
05:12
So we can pass in the points. So we’re getting the points that were originally passed in, we’re checking to make sure there are four, and then we’re passing it to the original constructor here, and then that becomes the points
here.
05:25
Then self.points
gets assigned to the instance as if it were in here.
05:32
So now if we save and run, you’ll see that we get the TypeError
still, even though we don’t have it in this function. So the way to fix this would be to replace all of these with points.
05:56
Say (0, 0)
… do a bit of copy/pasting …
06:03
make that (0, 1)
, (1, 1)
, (1, 0)
. Okay, so now we Control + S to save that and then F5 to run it, and we’re not getting any errors. And if we look at our box
, we’ll see that it’s a Square
object, and if we look at our points within that box, we can see that we have four Point
objects in there.
06:26
So that’s the super()
function. It doesn’t work just for the constructor. You can call any method that is on the parent class with the super()
function.
06:37
The super()
function can be quite confusing, so don’t worry if you don’t get it all just yet. There’s plenty more material to go through if you want to know more about super()
.
CastusVR on June 4, 2024
Hi Ash D,
good idea to move the check to the parent class :)
But there’s an issue of putting the check in the init method like that:
Let’s say you want to create a shape that doesn’t have it’s own class. In that case you need to instantiate the Shape class. By setting a default value of None for num_points, the check will raise the WrongNumberOfPoints error every time no num_points is given, because even an empty list of points has a length of 0 which is not None.
You could bypass that by always using len(points) as the second argument if you’re using the Shape class directly.
Another approach, especially if the check is a bit more complicated than this one, could be to create a new method for the check inside of the Shape class. In this example, I used a decorator to create a static method for checking the number of points, if needed:
...
class Shape:
def __init__(self, points):
for point in points:
if not isinstance(point, Point):
raise TypeError("All points should be members of the Point class!")
self.points = points
@staticmethod
def check_point_count(points, count):
if len(points) != count:
raise WrongNumberOfPoints
class Square(Shape):
def __init__(self, points):
super().check_point_count(points, 4)
super().__init__(points)
You can call every method of the parent class using super(), so that might be a possible approach to only write the code for the check once.
👉🏻 There is a tutorial on the Instance, Class and Static methods here on RealPython.
Siddhartha Koushik on July 6, 2024
Hi
If the shape class has the check whther it is a point, then why square or pentagon is not inheriting the check from the class? without using super function.
Thanks
Become a Member to join the conversation.
Ash D on Dec. 12, 2023
I guess you could also take the code de-duplication further by moving the point-quantity checking code into the parent class. Here’s how I think this could be done:
In Shape’s
__init__
, num_points could either be left as is, or could possibly be made safer by setting it to a default of None, so that it’s an optional argument, in case anyone ever instantiates Shape directly. It depends on whether the Shape class is ever likely to be instantiated directly, or if it will only ever be inherited from.Is this valid / worth doing?