Concrete Subclasses and Object Creation
An abstract class is not used to construct objects; it exists only as a basis for making subclasses and to express the properties they share.
Try it: Inheritance and Method Lookup
How a subclass inherits from its parent, how Python finds an attribute by walking the instance, then the class's method resolution order up to object, how overriding and super() work, why the same call does different things on different objects (polymorphism), and what isinstance / issubclass check.
How it works
- class Dog(Animal) gives Dog the MRO Dog → Animal → object; Puppy(Dog) gives Puppy → Dog → Animal → object.
- obj.name searches obj.__dict__, then each class of type(obj).__mro__ in order; the first class that defines the name wins, so a subclass method overrides the parent's.
- super().method() starts the search in the class after the one the current method was defined in, e.g. to run the parent's __init__.
- Calling the same method on a list of different objects runs each object's own version (polymorphism).
- isinstance(obj, C) is True when C is in type(obj).__mro__; if no class has the name, Python raises AttributeError.
Default run (63 steps): Ready: Animal, Dog, Cat and Puppy, then the statements you picked. Each attribute is searched along the class's MRO. … Finished. Printed: Rex the dog says Woof / Tom the cat says Meow / Bo the dog says Woof! / True / Puppy / Dog / Animal / object.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Single inheritance only (the lab's MRO code is Python's C3 algorithm, but no class here has two parents).
Loading the simulation…
A Drawing Program Raises a Design Question
Imagine a drawing program that shows several kinds of shapes on screen: rectangles, ovals, and rounded rectangles. Each of these knows how to paint itself, and each does so through its own redraw() method. All of these shapes share a common superclass, which we can call Shape. The question this article follows is simple to state and surprisingly deep: what should redraw() do inside Shape itself?
The answer turns out to be a useful idea in object-oriented design. Some classes exist only to describe what their subclasses have in common, and they are never meant to be turned into objects. Working through why that is true will clarify the purpose of abstract classes and abstract methods.
What do you think happens?
Suppose Shape declares no redraw() method at all, and a program holds a variable of type Shape and calls redraw() on it. What happens?
Reveal answer
The compiler rejects the call, because it checks calls against the declared type Shape, and Shape has no redraw() to check against.
The compiler checks these calls against the declared type, not against the object that happens to be stored there. So the method must be declared in Shape even if its body is empty.
Tracing Which redraw() Actually Runs
Whenever a particular shape must draw itself, the redraw() method belonging to that shape's own class is the one that runs. A rectangle is drawn by the redraw() in Rectangle, and an oval is drawn by the redraw() in Oval. The redraw() inside Shape is therefore never the one doing the drawing for a rectangle or an oval.
That raises the puzzle. If Shape's version of redraw() will never really be used, what should it contain? The source suggests the honest answer is to leave the body empty. The class Shape stands for the general notion of a shape, and there is no real way to draw a general notion. Only specific shapes, such as a rectangle or an oval, can actually be drawn.
The method cannot simply vanish, though. Other code in Shape, such as the setColor() method, may call redraw(). A program may also hold a variable declared as Shape and call redraw() on it. Because the compiler checks these calls against the declared type, Shape must declare redraw() even though its body has nothing to do.
Abstract Methods and Abstract Classes
The idea that Shape and its redraw() method are abstract in meaning can be stated to the compiler. You do this by adding the modifier abstract to both the class definition and the method definition. The modifier makes the abstractness visible in the code, so that the compiler can enforce the rules that go with it.
An abstract class is a class that is not used to construct objects. It exists only as a basis for making subclasses and to express the properties they share.
An abstract method has a particular shape of its own. Where an ordinary method would have a block of code giving its implementation, an abstract method has only a semicolon in that place. The method is declared, so that calls to it are legal, but no implementation is supplied at that level of the class hierarchy.
- The abstract modifier appears on the class and on the method.
redraw()ends with a semicolon instead of an implementation block.- Rectangle is not abstract, so it is concrete, and it supplies the body that Shape left out.
The responsibility does not disappear when the body is omitted. It moves downward. Every concrete subclass of the abstract class must provide an implementation for each abstract method it inherits. The abstract class declares that the operation exists, and each concrete subclass says how it works.
Following a Triangle Into the Program
To see the rules applied, imagine a new kind of shape is added to the drawing program: a triangle. The programmer creates a Triangle class that extends Shape. Because Shape declares redraw() as abstract, Triangle is obliged to supply a redraw() implementation before it can be a concrete class.
The figure shows the rule in motion. The abstract declaration in Shape creates an obligation that passes to Triangle. Until Triangle supplies a body for redraw(), the program will not accept an attempt to create a Triangle object. Once the body is written, Triangle becomes concrete and its objects can be built and drawn.
Mistakes Beginners Make With Abstract Classes
Common mistake Trying to build an object of the abstract class itself
What it looks likeShape s = new Shape(); used as if it were a normal class.
Why it failsShape is abstract. It exists only as a basis for subclasses, and a plain shape with no particular form cannot do anything useful.
Do this insteadCreate objects from a concrete subclass such as Rectangle, and store them in a Shape variable if needed.
Common mistake Deleting the abstract method from the superclass because its body is empty
What it looks likeRemoving redraw() from Shape since Shape's version never runs.
Why it failsCalls through a Shape variable are checked against the declared type, so without the declaration those calls are rejected.
Do this insteadKeep the abstract declaration with its semicolon, so the operation is declared and every concrete subclass is obliged to implement it.
Common mistake Assuming an abstract method has an implementation somewhere in the superclass
What it looks likeExpecting Shape's redraw() to draw a generic shape when called on a Rectangle.
Why it failsThe
redraw()that runs for a rectangle is the one belonging to Rectangle's own class, not Shape's.Do this insteadWrite the drawing logic in each concrete subclass that needs it.
Common mistake Forgetting that a new subclass must implement inherited abstract methods
Practice Predicting Concrete Obligations
A new class Hexagon extends an abstract class Shape that declares redraw() as abstract. Hexagon is written with no redraw() method at all. Can objects of Hexagon be created, and what must be added before it becomes a concrete class?
Hints
- Ask which classes are concrete and which can produce objects.
- Recall what every concrete subclass must do with each inherited abstract method.
Explain in your own words why a Shape variable can hold a Rectangle but you cannot write new Shape() to create a plain shape.
Hints
- Think about which class the object behind the variable belongs to.
- Think about what a plain shape with no particular form can do.
Key Takeaways
- A class that is abstract is not used to construct objects; it exists as a basis for subclasses and to express the properties they share.
- A class that is not abstract is concrete, and only concrete classes can be used to create objects.
- An abstract method is declared with the abstract modifier and ends with a semicolon instead of an implementation, so the operation is declared without a body at that level.
- Every concrete subclass of an abstract class must provide an implementation for each abstract method it inherits, which is how the abstract declaration becomes working behavior.
- A variable of the abstract type can refer to any concrete subclass object, so callers can work with a Shape without knowing whether it is a Rectangle or an Oval.