object
SuperclassIn our very first chapter, we described every piece of data as an
object, and have continued to use this term throughout this
course. It turns out that “object” is not merely a theoretical concept,
but made explicit in the Python language. Python has a special class
called object
, which is an ancestor
classBy “ancestor” we mean either a parent class, or a
parent of a parent class, etc. of every other class: built-in
classes like int
, our custom data classes and the classes
we’ve defined in this chapter, including the abstract Stack
class.
By default, whenever we define a new class (including data classes),
if we do not specify a superclass in parentheses, object
is
the implicit superclass, which is why we can write
class Stack:
instead of
class Stack(object):
.
object
special
methodsThis object
class defines several special methods as
part of its shared public interface, including two we have seen already
this
chapter: The Python convention is to name methods that have a
special purpose with double underscores. These are sometimes called
“dunder” methods (short for double
underscore).
__init__(self, ...)
, the initializer__str__(self)
, which returns a str
representation of the objectUnlike our Stack
abstract class earlier this chapter,
the object
class is actually not abstract, and
implements each of these methods. We can use this to illustrate a
different form of inheritance, where the superclass is a concrete class.
In this case, inheritance is used not just to define a shared public
interface, but also to provide default implementations for each
method in the interface.
For example, suppose we create a dummy class with a completely empty body:
class Donut:
"""A donut, because why not?"
This class inherits the object.__init__
method, which
allows us to create new Donut
instances.
>>> donut = Donut()
>>> type(donut)
<class '__main__.Donut'>
Similarly, this class inherits the object.__str__
method, which returns a string that states the class name and memory
location of the object:
>>> d = Donut()
>>> d.__str__()
'<__main__.Donut object at 0x7fc299d7b588>'
We can use the built-in dir
function to see all of the
special methods that Donut
has inherited from
object
:Though this list includes few special attributes set
directly by the Python interpreter, which are beyond the scope of this
course.
>>> dir(Donut)
'__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] [
There is another reason these methods are special beyond simply being
inherited from the object
superclass: they are often called
by other functions or parts of Python syntax. For example, we have
already seen how the __init__
method is called when a new
object is initialized.
The __str__
method is called when we attempt to convert
an object to a string by calling str
on it:
>>> d = Donut()
>>> d.__str__()
'<__main__.Donut object at 0x7fc299d7b588>'
>>> str(d)
'<__main__.Donut object at 0x7fc299d7b588>'
Similarly, the built-in print
function actually first
converts its arguments into strings using their __str__
methods, and then prints out the resulting text.
Now, even though the object
superclass contains default
implementations of __init__
and __str__
, in
practice we often want to define our own custom implementations of these
methods.
Every time we’ve defined our own __init__
in a class, we
have overridden the object.__init__
method. Formally, we say that a class C
overrides a method m
when the method
m
is defined in the superclass of C
, and is
also given a concrete implementation in the body of
C
. This definition applies whether the superclass of
C
has m
as an abstract or concrete method. For
example, we could say that Stack1
overrides the
push
and pop
method from its abstract
superclass Stack
.
Similarly, when we defined a custom exception class in 10.6 Exceptions as a Part of the Public Interface,
class EmptyStackError(Exception):
"""Exception raised when calling pop on an empty stack."""
def __str__(self) -> str:
"""Return a string representation of this error."""
return 'pop may not be called on an empty stack'
this class overrode the __str__
method to use
its own string representation, which is displayed when this exception is
raised.