Python generics

Introductory article on python generics

Search for: Introductory article on python generics

Generics in python: Kansas State University book

  1. quality: medium
  2. good introduction
  3. practical use

Clear as mud: Python reference doc on it


#need for old generics < 3.5
from typing import List

def tesGenericLists():
    oldlist: List[int] = [1,2,3] # This is an old generics
    newlist: list[int] = [4,5,6] # new one is native
    print(oldlist)
    print(newlist)
  1. Look at list[Blah]
  2. The "list" class in Python now is a generic class that takes "Blah" as its type input.
  3. It is generics out of the box

#variable definitions
x: SomeGenericClass[Type]

#class inheritance
class SomeClass(SomeGenericClass[Type])

"""
*************************************************
* Explain generics: lesson 2
* 
* 1. You have to enable strick typing
* 2. MyList2(list[Person]): means I am extending person list
* 3. MyList(Generic[Person]): will not work
* 4. MyList(Generic[TypeVar("T", bound=Person)]) will work
* 5. See code below
*
*************************************************
"""
from baselib import baselog as log

from typing import TypeVar, Generic
class Person():
    def __init__(self, name: str):
        self.name = name
    def __repr__(self) -> str:
        return self.name


#
# You are saying my class works with "person" objects
# It is NOT saying I am a list of Persons
# Note: you have to enable strict typing to see the implications
#

T = TypeVar ("T", bound=Person)
class MyList(Generic[T]):
    persons: list[T] = []
    def addPerson(self, obj: T):
        self.persons.append(obj)

# Extending a list of persons
class MyList2(list[Person]):
    pass

"""
# won't work
class MyList3(Generic[Person]):
    pass
    
"""

def testPList():
    personList = MyList[Person]()
    #This will not work below if strict typing is enabled
    #personList.addPerson(1)
    personList.addPerson(Person(name="satya"))
    print(personList.persons)

def testPList2():
    personList = MyList2()
    #fails
    #personList.append(1234)
    personList.append(Person(name="satya"))
    print(personList)

def localTest():
    log.ph1("Starting local test")
    testPList()
    testPList2()
    log.ph1("End local test")

if __name__ == '__main__':
    localTest()

Although works, doesn't seem like a good example!!


from typing import Generic, TypeVar

# Define two type variables, T and U, which can be any types
T = TypeVar('T')
U = TypeVar('U')

# Define a generic class Pair, which can hold two items of types T and U
class Pair(Generic[T, U]):
    def __init__(self, first: T, second: U):
        self.first = first
        self.second = second

    def get_first(self) -> T:
        return self.first

    def get_second(self) -> U:
        return self.second

    def __repr__(self) -> str:
        return f'Pair({self.first!r}, {self.second!r})'

# Example usage
if __name__ == "__main__":
    # Pair of an integer and a string
    pair_int_str = Pair(1, "Apple")
    print(pair_int_str)

    # Pair of a string and a list of floats
    pair_str_list = Pair("Temperatures", [32.5, 31.8, 30.2])
    print(pair_str_list)

    # Accessing elements
    print(pair_int_str.get_first())  # Output: 1
    print(pair_int_str.get_second())  # Output: Apple
    print(pair_str_list.get_second())  # Output: [32.5, 31.8, 30.2]

from typing import Generic, TypeVar

# Define base classes
class BaseT:
    pass

class BaseU:
    pass

# Define type variables with bounds
T = TypeVar('T', bound=BaseT)
U = TypeVar('U', bound=BaseU)

# Define a generic class Pair with type variables T and U
class Pair(Generic[T, U]):
    def __init__(self, first: T, second: U):
        self.first = first
        self.second = second

    def get_first(self) -> T:
        return self.first

    def get_second(self) -> U:
        return self.second

    def __repr__(self) -> str:
        return f'Pair({self.first!r}, {self.second!r})'

# Example subclasses of BaseT and BaseU
class DerivedT(BaseT):
    pass

class DerivedU(BaseU):
    pass

# Usage with derived types
pair = Pair(DerivedT(), DerivedU())
print(pair)

Read the 646 spec

  1. The collection classes are already generic
  2. The sytax for making use of already defined generic classes
  3. Extending a genricised class with a specific type
  4. Defining a class that works with its own types
  5. Defining a class that works with only certain base type of classes

Total list of Python Enhancement Proposals (PEP) for typing

Basics of typehints

585 Type hinting for generics

646 PEP Generics

Standard library Documentation on TypeVar