Inheritance in Python
00:00
You’re almost always using some form of inheritance within Python, even if you don’t explicitly declare it. To demonstrate that, I’m going to use the Python interactive shell. I’m going to start by creating a new class called MyClass
—a very creative name.
00:19
I want to leave this class blank, and so I’ll just write pass
here. pass
is a special keyword that will allow you to create a function, class, or loop that is effectively empty.
00:33
Now, I will instantiate this class into a new object called c
. Python has a built-in function called dir()
, which returns a list of all the members of the class you pass in. A class’s members are just the attributes and methods that make up the class, including the special .__init__()
method that’s used for object instantiation.
00:59 But we didn’t declare any attributes or methods, so this class has no members, right? Let’s find out.
01:08
That’s probably not what you were expecting. What are all of these members with double underscores (__
), and where are they coming from? These members are actually all methods called magic methods, or dunder methods,
01:23
named after their double underscores. Some of these may look familiar to you, such as __init__
, which we use as the constructor for constructing new classes.
01:35
We also used __init__
to declare instance attributes. To explain where these are coming from, let me create one more object and list its members.
01:49
And would you look at that? It looks like the o
object and the c
object have very similar members, except the c
object—which came from our custom class—has some extra members, like __weakref__
.
02:04
You might be able to guess what’s going on here, but let me explain. This o
object was instantiated from the object
class, which is super confusing considering we’ve been calling every instance of any class an object. Well, this object
class is not a class that we define ourselves.
02:24
It’s built into Python and it’s this object
class that defines these strange members that we’re seeing. And, as it turns out, every custom class that we create automatically inherits from this object
class behind the scenes, even if we don’t explicitly make it.
02:45
That explains why the c
object has all of these members too—it inherited it from the object
class. The extra members present in our custom class came from elsewhere, but that’s beyond the scope of this course.
03:00
The point is every class in Python implicitly inherits from this object
superclass, which defines all these magic methods that Python uses to manage our objects.
03:14 Another place where inheritance is used is with exceptions. Exception objects are special in that they can be thrown up a call stack and caught anywhere along that stack.
03:27 If no code catches that exception object before it reaches the top, the program crashes and you’re left with a stack trace. If you’re interested in learning more about exceptions, we have a course for that, which I will link down in the video notes below.
03:45 Let’s see how they work with inheritance. Someone new to exceptions might try to create their own exception class as if it were a normal class, just like this.
03:59
Exceptions can be raised with the raise
keyword. This is similar to throw in other languages. All right, we got a stack trace—but it’s not from our exception.
04:12
There’s a TypeError
—not MyError
. Python has identified that we’re trying to create a custom exception class, but we didn’t do it right.
04:23
It’s saying that our class must inherit from BaseException
. BaseException
is a built-in class provided for all error types. There are lots of classes that derive from BaseException
, such as SyntaxError
and ArithmeticError
, which means that we can use them in place of BaseException
.
04:47
Remember, if SyntaxError
inherits from BaseException
, it means SyntaxError
is a BaseException
because it inherits all of the members of BaseException
. What specific exception type you inherit from depends on the situation at hand. BaseException
is not supposed to be inherited from directly, so we’ll choose its next child, a class called Exception
.
05:16
This is the class that the official documentation recommends inheriting from. I’ll redefine the MyError
class, but this time I’ll put the name of the class I want to inherit from in parentheses, after the class name.
05:33
MyError
is now an Exception
, literally. I could write pass
in here and leave the custom exception type blank,
05:42
but I want to do one more thing. Exceptions usually have some sort of message that’s intended to tell the user or developer what went wrong. The Exception
class contains an attribute for the message,
05:57
and as such, it’s been passed down to our MyError
class. To use it, we first must add an .__init__()
method to this class. This exception will need to be passed a message
when it is raised.
06:15
Now, we can use the super()
function to call the .__init__()
method of Exception
,
06:22
passing in the message
. In essence, when we create a MyError
object, we’re asking for a message
and then passing it to the parent.
06:33
Finally, now that we’re done writing our class, we can raise MyError
, passing in whatever message
we want. I’ll choose a particularly unhelpful one that you might’ve seen before.
06:47
There’s our custom exception with the message. This was possible due to inheritance. We created a custom class that inherited from Exception
, so Python recognized our class as an exception.
07:02
We even utilized the inherited .message
attribute to allow our custom exception class to be utilized with a custom message.
Subhash Bhushan on June 26, 2020
He could have been referring to this: realpython.com/courses/introduction-python-exceptions/
manohaard on July 27, 2020
where can I find all your courses?? pls provide link..
Dan Bader RP Team on July 27, 2020
You can click on an instructor’s name to see all of their courses. For example, all of Austin’s courses are listed here on his team profile: realpython.com/team/acepalia/
muondude on Jan. 13, 2022
I found this section on inheritance a bit hard to follow. Why did you not have to create an instance of MyError?
Bartosz Zaczyński RP Team on Jan. 13, 2022
@muondude He did create an instance of MyError
by calling it with a pair of parentheses. Note that in Python, you can raise either an exception class or its instance:
>>> class MyError(Exception):
... pass
...
>>> # Class:
>>> raise MyError
Traceback (most recent call last):
File "<input>", line 1, in <module>
raise MyError
MyError
>>> # Class instance:
>>> raise MyError("Details about the error...")
Traceback (most recent call last):
File "<input>", line 1, in <module>
raise MyError("Details about the error...")
MyError: Details about the error...
It makes sense to raise an instance of an exception class when you need to provide some context around the error that occurred.
mp on April 25, 2023
Just curious, is it true to say that, to use the MyError
class “we must” add .__init__()
? Without it, wouldn’t it just use .__init__()
in the parent class anyway?
Bartosz Zaczyński RP Team on April 26, 2023
@malcolmperfect You’re right. Technically, you don’t have to implement your own .__init__()
method in a subclass unless you want to change its paramater list or modify the behavior somehow. The following code will work just fine:
>>> class MyError(Exception):
... pass
...
>>> MyError("Something went wrong")
MyError('Something went wrong')
Become a Member to join the conversation.
leilanobatova on June 26, 2020
Austin said in video that link to the course about Exceptions will be under the video, I couldn’t find one. It would be super helpful to have one here. Thanks!