Abstract classes#

Abstract classes in Object Oriented Programming (OOP) are classes that cannot be instantiated. In Python, abstract classes can be by inheriting the abc.ABC class. Abstract classes also need to have abstract methods, otherwise Python will not treat the class as abstract. Trying to create an instance (object) from an abstract class results in a TypeError exception.

from abc import ABC, abstractmethod

class MyAbstractClass(ABC):
    @abstractmethod
    def some_method(self):
        pass

class MyClass(MyAbstractClass):
    def some_method(self):
        return 5 * 5

# OK
my_instance = MyClass()

# TypeError: Can't instantiate abstract class MyClass with abstract method some_method
my_abstract_instance = MyAbstractClass()

Let’s modify our example from Polymorphism.

 1from abc import ABC, abstractmethod
 2
 3import tkinter as tk
 4import tkinter.ttk as ttk
 5import tkclasswiz as wiz
 6
 7# An abstract class
 8class Wheel(ABC):
 9    def __init__(self, diameter: float):
10        self.diameter = diameter
11
12    @abstractmethod
13    def get_info(self) -> str:
14        pass
15
16class WinterWheel(Wheel):
17    def get_info(self) -> str:
18        return "Wheel for winter."
19
20class SummerWheel(Wheel):
21    def get_info(self) -> str:
22        return "Wheel for summer."
23
24
25class Car:
26    def __init__(self, name: str, speed: float, wheels: list[Wheel]):
27        self.name = name
28        self.speed = speed
29        self.wheels = wheels
30
31        if speed > 50_000:
32            raise ValueError("Car can go up to 50 000 km / h")
33
34        if len(wheels) != 4:
35            raise ValueError("The car must have 4 wheels!")
36
37# Tkinter main window
38root = tk.Tk("Test")
39
40# Modified tkinter Combobox that will store actual objects instead of strings
41combo = wiz.ComboBoxObjects(root)
42combo.pack(fill=tk.X, padx=5)
43
44def make_car(old = None):
45    """
46    Function for opening a window either in new definition mode (old = None) or
47    edit mode (old != None)
48    """
49    assert old is None or isinstance(old, wiz.ObjectInfo)
50
51    window = wiz.ObjectEditWindow()  # The object definition window / wizard
52    window.open_object_edit_frame(Car, combo, old_data=old)  # Open the actual frame
53
54def print_defined():
55    data = combo.get()
56    data = wiz.convert_to_objects(data)  # Convert any abstract ObjectInfo objects into actual Python objects
57    print(f"Object: {data}; Type: {type(data)}",)  # Print the object and it's datatype
58
59
60# Main GUI structure
61ttk.Button(text="Define Car", command=make_car).pack()
62ttk.Button(text="Edit Car", command=lambda: make_car(combo.get())).pack()
63ttk.Button(text="Print defined", command=print_defined).pack()
64root.mainloop()

We can see that the Wheel is now an abstract class. It is then inherited by WinterWheel and SummerWheel. If we try to define the wheels parameter of our Car object, only these two inherited classes will be definable.

../_images/new_define_frame_list_abstractclass.png

We can see that while WinterWheel and SummerWheel are definable (due to Polymorphism), Wheel is not.