Python Generic Types: Tutorial & Examples

Updated: March 4, 2023 By: Goodman Post a comment

This concise article is about generic types in Python.

Overview

Generic types allow us to define classes, functions, or methods that can work with different data types without specifying the exact data type beforehand. This is useful when we want to create a single implementation that can handle multiple data types.

To define generic types in Python, we can use the built-in module typing, which provides a set of type hints for Python 3.5 and newer. The typing module defines a number of generic types, such as List, Tuple, Dict, and Union, which can be used to define generic functions, methods, and classes.

Words might be boring and vague; let’s examine the following practical examples for a better understanding.

Examples

Generic Function

In this basic example, we’ll define a generic function that can work with different types of lists:

from typing import List, TypeVar

T = TypeVar('T')

def reverse_list(lst: List[T]) -> List[T]:
    return lst[::-1]

# Example usage
num_lst = [1, 2, 3, 4, 5]
str_lst = ['a', 'b', 'c', 'd', 'e']
print(reverse_list(num_lst)) 
print(reverse_list(str_lst))

Output:

[5, 4, 3, 2, 1]
['e', 'd', 'c', 'b', 'a']

Basic Generic Class

This example is a little bit more advanced than the first one (but still simple in comparison to the later ones). We’ll define a generic class that can work with different types of values:

from typing import TypeVar

T = TypeVar('T')

class Box:
    def __init__(self, value: T):
        self.value = value

    def get_value(self) -> T:
        return self.value

# Example usage
box1 = Box(10)
box2 = Box('Sling Academy')
print(box1.get_value()) 
print(box2.get_value()) 

Output:

10
Sling Academy

Generic Dictionaries

In this example, we’ll create a generic method that can work with different types of dictionaries:

from typing import Dict, TypeVar

K = TypeVar('K')
V = TypeVar('V')

def print_dict(dct: Dict[K, V]) -> None:
    for key, value in dct.items():
        print(f'{key}: {value}')

# Example usage
num_dict = {1: 'one', 2: 'two', 3: 'three'}
str_dict = {'a': 1, 'b': 2, 'c': 3}
print_dict(num_dict) 
print_dict(str_dict) 

Output:

1: one
2: two
3: three
a: 1
b: 2
c: 3

Generic Data Repository Class

This example demonstrates a more advanced use case of generic types in Python. We’ll make a generic data repository class that can work with different data types.

from typing import TypeVar, Generic, List

T = TypeVar('T')

class DataRepository(Generic[T]):
    def __init__(self):
        self.data = []

    def add_data(self, item: T) -> None:
        self.data.append(item)

    def remove_data(self, item: T) -> None:
        self.data.remove(item)

    def get_all_data(self) -> List[T]:
        return self.data

# Example usage
repo = DataRepository[int]()
repo.add_data(10)
repo.add_data(20)
repo.add_data(30)
print(repo.get_all_data()) # Output: [10, 20, 30]
repo.remove_data(20)
print(repo.get_all_data()) # Output: [10, 30]

repo2 = DataRepository[str]()
repo2.add_data('apple')
repo2.add_data('banana')
repo2.add_data('orange')
print(repo2.get_all_data()) # Output: ['apple', 'banana', 'orange']
repo2.remove_data('banana')
print(repo2.get_all_data()) # Output: ['apple', 'orange']

Our DataRepository class uses a generic type T to define the type of data that the repository can store. The generic type T ensures that the data added to the repository is of the correct type, and the generic return type List[T] ensures that the data retrieved from the repository is of the correct type, too.

Benefits of Using Generic Types

Benefits of using generic types include increased flexibility and reusability of code, improved type safety and clarity, and reduced development time.