Python generics are type hints in Python. that allow you to write functions and classes that can work with any data type. In this article, we will discuss Python Generics with code examples.
What are Python Generics?
Python generics are like hints in Python. They came out in Python 3.5 and newer versions. They let you say what type of things your variables, functions, and results are. It helps you write down what kind of things your code is dealing with, but it doesn't make the computer check it while running the code. This makes your code easier to read and work with, especially when you are working on big projects or with other people.
Python Generics Example
Below, are examples of Python Generics in Python.
Example 1: Python Generics with Function
In this example, the below code defines a generic function element
that retrieves the first element from a list of any type. It employs Python's TypeVar
to denote the placeholder type T
. The function is demonstrated with integer and string lists, printing their respective first elements.
from typing import TypeVar, List
T = TypeVar('T')
def element(items: List[T]) -> T:
return items[0]
# Usage
print(element([1, 2, 3]))
print(element(['a', 'b', 'c']))
Output
1
a
Example 2: Python Generics with Different Data Types
In this example, below code define a generic function combine
that concatenates two different data types T
and U
into a string. It offers flexibility for combining diverse data types while ensuring type safety. Examples showcase combining integers and strings, with concatenated strings
from typing import TypeVar
T = TypeVar('T')
U = TypeVar('U')
def combine(a: T, b: U) -> str:
return str(a) + str(b)
# Usage
print(combine(10, 20))
print(combine('hello', 2020))
Output
1020
hello2020
Example 3: Python Generics with Class
In this example, code defines a generic class Container
using TypeVar
and Generic
, allowing it to hold any type of content. Instances container_int
and container_str
are created, holding an integer and a string respectively, and their content is retrieved using the retrieve_content
method.
from typing import TypeVar, Generic
T = TypeVar(Generic[T])
class Container:
def __init__(self, content: T):
self.content = content
def retrieve_content(self) -> T:
return self.content
# Usage
container_int = Container(10)
container_str = Container('GeeksforGeeks')
print(container_int.retrieve_content())
print(container_str.retrieve_content())
Output
10
GeeksforGeeks
Python Generics with Duck Typing
Duck typing and Python generics are two different concepts, but they can be related in how they allow for flexible and dynamic programming in Python.
For know More about Duck Typing Click Here
Now, let's see how these two concepts can be related with an example:
Python does not have the concept of generics before Python3.5, however you might argue that every function is generic because the parameters are not typed. This is a duck typing approach, which means that anything that walks and quacks like a duck is regarded as such. So, normally, "generic" functions would just verify if the parameters or objects had the bare minimum of required properties and then handle the data accordingly.
Example : In below code, Python generics enable type constraints for functions like process_data
, ensuring that inputs conform to expected types; meanwhile, duck typing allows objects to be treated as the required type based on their behavior, irrespective of their explicit type, as demonstrated in process_data
's dynamic handling of objects with a quack
method.
from typing import TypeVar, Iterable
# Declare type variable
T = TypeVar('T')
def process_data(data: Iterable[T]) -> None:
for item in data:
# Check if the object has a quack method, then call it
if hasattr(item, 'quack') and callable(item.quack):
item.quack()
else:
print(f"This object of type {type(item).__name__} doesn't quack like a duck!")
# Example class
class Duck:
def quack(self):
print("Quack!")
# Another class with similar behavior
class AnotherBird:
def quack(self):
print("Quack!")
# Using duck typing with process_data function
duck_obj = Duck()
another_bird_obj = AnotherBird()
# Both objects "quack" like a duck
duck_list = [duck_obj, another_bird_obj]
# We can pass duck_list to process_data because both objects have a quack method
process_data(duck_list)
Output
Quack!
Quack!