· coding · 4 min read
Python: Dataclasses, Abstract Base Classes (ABC), and Object-Oriented Programming (OOP)

Abstract Base Classes
An abstract base class specifies the interface that a class should adhere to and acts as an agreement between different parts of a program.
Q: Import the abstract base class and abstract method.
from abc import ABC, abstractmethod
Q: Define a short abstract base class, “HelloWorld”, with an abstract method called “hello” that should return a string.
from abc import ABC, abstractmethod
class HelloWorld(ABC):
@abstractmethod
def hello(self) -> str:
pass
Attempting to create an instance of an abstract base class (ABC) raises an error. You can, however, create subclasses that inherit from the ABC. An ABC purely defines the kind of methods that a class that inherits from the ABC should have.
# Assume the ABC, HelloWorld, is implemented in the same file.
class JapaneseHelloWorld(HelloWorld):
def hello(self) -> str:
return "こんにちは、世界!"
class ChineseHelloWorld(HelloWorld):
def hello(self) -> str:
return "你好世界!"
Attempting to create an instance of one of the concrete classes, “JapaneseHelloWorld” and “ChineseHelloWorld”, without implementing the “hello” method would raise an error too.
Data classes
Python’s dataclass
functionality allows you to write shorter code and initialize, print, compare, and order data much more easily.
Why use data classes?
A class for a data container is often used differently. Many instances may need to be made. You probably want to order them, compare them, easily inspect the data inside them, etc.
Regular classes don’t provide much built-in convenient functionality for data oriented classes. Classes that build around a behavior may not have as many instances or be conducive to representing data structures. Because of this, some programming languages provide a type of class that is better tuned to storing data, e.g. C#‘s struct type that is good for representing data structures.
Simplest data class: All required arguments
Data classes have a built-in initialize that will help you quickly fill in an object with data. There are easy ways to print compare and order them.
# Regular class
class Person:
"""A person that has a name, job, and age."""
name: str
job: str
age: int
def __init__(self, name, job, age):
self.name = name
self.job = job
self.age = age
p1 = Person(name='Joe', job='SDE', age=25)
Q: Implement this as a dataclass
:
from dataclasses import dataclass
@dataclass
class Person:
"""A person that has a name, job, and age."""
name: str
job: str
age: int
Data class with default arguments
Let’s say all of our people usually have the same job title, which is software development engineer (“SDE”). We’d want to set this as a default parameter to save some typing.
from dataclasses import dataclass
@dataclass
class Person:
"""A person that has a name, job, and age."""
name: str
job: str = "SDE"
age: int
p1 = Person(name='Joe', age=25)
Data class with attributes that depend on other parameters
Dynamically generated attributes can be defined in data classes using the field
functionality in combination with __post_init__
.
For example, let’s say that the ‘age’ parameter automatically specifies a job if it’s low enough. Otherwise, ‘job’ will default to “SDE” like before.
from dataclasses import dataclass, field
@dataclass
class Person:
"""A person that has a name, job, and age."""
name: str
job: str = field(default = "SDE", init = False)
age: int
def __post_init__(self):
age = self.age
if (self.job == "SDE") and (age <= 18):
if age >= 14 and age <= 18:
self.job = "Student - high school"
elif age >= 12 and age <= 14:
self.job = "Student - middle school"
elif age >= 5 and age <= 12:
self.job = "Student - elementary school"
else:
self.job = "Baby, toddler, or pre-K"
Reference: ArjanCodes. 2021. If you’re not using Python DATA CLASSES yet, you should. 🚀 [YouTube]
Prefer composition over inheritance
Inheritance makes it difficult to duplicate functionality to different classes.
With composition, our goal is to separate out concepts so that they can be combined in meaningful ways. We’re not creating hierarchies of classes.
If we have a class that we want to make subclasses of, it’s beneficial to make the first one an abstract base class.
from abc import ABC, abstractmethod
from typing import Optional
class FFNN(ABC):
"""Represents a PyTorch module for a feed-forward neural network."""
@abstractmethod
def forward()
"""TODO"""
Reference: ArjanCodes. Why COMPOSITION is better than INHERITANCE - detailed Python example. 2021. [YouTube]
NamedTuple
and TypedDict
PEP 589 – TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys
Representing an object or structured data using (potentially nested) dictionaries with string keys (instead of a user-defined class) is a common pattern in Python programs. Representing JSON objects is perhaps the canonical use case, and this is popular enough that Python ships with a JSON library. This PEP proposes a way to allow such code to be type checked more effectively.
from typing import TypedDict
class Movie(TypedDict):
name: str
year: int
# a type checker should accept this code:
movie: Movie = {'name': 'Blade Runner',
'year': 1982}