Python generics

satya - 2/19/2024, 12:01:52 PM

Introductory article on python generics

Introductory article on python generics

Search for: Introductory article on python generics

satya - 2/19/2024, 12:18:45 PM

Generics in python: Kansas State University book

Generics in python: Kansas State University book

satya - 2/19/2024, 12:19:25 PM

it has

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

satya - 2/19/2024, 12:26:06 PM

Clear as mud: Python reference doc on it

Clear as mud: Python reference doc on it

satya - 2/19/2024, 1:02:42 PM

An example


#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)

satya - 2/19/2024, 1:03:32 PM

Key point

  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

satya - 2/19/2024, 1:07:19 PM

Various syntaxes


#variable definitions
x: SomeGenericClass[Type]

#class inheritance
class SomeClass(SomeGenericClass[Type])

satya - 2/19/2024, 1:49:37 PM

An example with lists


"""
*************************************************
* 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()

satya - 2/19/2024, 1:53:56 PM

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

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

satya - 2/19/2024, 2:48:01 PM

Example of a generic pair


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]

satya - 2/19/2024, 2:48:51 PM

Example of specific type of pairs


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)

satya - 2/19/2024, 4:15:01 PM

Read the 646 spec

Read the 646 spec

satya - 2/19/2024, 4:15:31 PM

What I know so far

  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

satya - 2/21/2024, 3:10:58 PM

Total list of Python Enhancement Proposals (PEP) for typing

Total list of Python Enhancement Proposals (PEP) for typing

satya - 2/21/2024, 3:13:39 PM

Basics of typehints

Basics of typehints

satya - 2/21/2024, 3:30:57 PM

585 Type hinting for generics

585 Type hinting for generics

satya - 2/21/2024, 3:32:54 PM

646 PEP Generics

646 PEP Generics

satya - 2/21/2024, 3:35:45 PM

Standard library Documentation on TypeVar

Standard library Documentation on TypeVar