Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Python Generic Type Parameters

Learn Python type parameter syntax for generic functions, classes, and type aliases, including type-parameter scope, bounds, defaults, and the roles of TypeVarTuple and ParamSpec.

Levels: Advanced ~22 min read

Advanced

Python lets you make functions, classes, and type aliases generic by putting a type parameter list after their name. This gives one definition a type parameter such as T that can represent different types.

A generic function

Put a type parameter in square brackets immediately after the function name. The parameter can then be used in the function annotations.

python generic-function.py
1
2
3
4
5
def first[T](items: list[T]) -> T:
    return items[0]

print(first([10, 20, 30]))
print(first(["Ada", "Grace"]))

The same function definition is generic over T, so T can represent the element type for different calls.

A generic class

A class can declare a type parameter in the same position. The parameter is then available in annotations inside the class body.

python generic-class.py
1
2
3
4
5
6
7
8
9
class Box[T]:
    def __init__(self, value: T):
        self.value = value

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

print(Box(42).get())
print(Box("ready").get())

T is available throughout the generic class body, including method annotations.

A generic type alias

The type statement from the previous lesson can also have type parameters.

python generic-alias.py
1
2
3
4
type Pair[T] = tuple[T, T]

point: Pair[int] = (3, 4)
print(point)

The alias is parameterized with int when it is used in an annotation.

Use more than one type parameter

Separate type parameters with commas when a result relates more than one type.

python multiple-parameters.py
1
2
3
4
5
def pair[T, U](left: T, right: U) -> tuple[T, U]:
    return left, right

print(pair(7, "seven"))
print(pair("name", True))

T describes the first value and U describes the second, so their types do not have to be the same.

Type parameters have their own scope

A type parameter belongs to the generic function, class, or alias that declares it. It is not automatically a name in the surrounding module scope.

python type-parameter-scope.py
1
2
3
4
5
6
7
8
def identity[T](value: T) -> T:
    return value

print(identity.__type_params__[0].__name__)
try:
    print(T)
except NameError:
    print("T is not a module-level name")

The type parameter is attached to the generic object; T is not created as an ordinary module-level variable.

A type parameter can have a bound

A type variable can have a bound after a colon. The bound describes the types a type checker may allow for that parameter.

python type-parameter-bound.py
1
2
3
4
5
6
7
def echo_int[T: int](value: T) -> T:
    return value

parameter = echo_int.__type_params__[0]
print(parameter.__name__)
print(parameter.__bound__.__name__)
print(echo_int(7))

The bound is available on the type-parameter object. The syntax does not insert a runtime type check into the function.

Python 3.13+ also supports type-parameter defaults

A type parameter may have a default expression after =. This affects type-parameter substitution for type-checking, and the default is available from the type-parameter object.

python type-parameter-default.py
1
2
3
4
5
6
def make_value[T = int](value: T) -> T:
    return value

parameter = make_value.__type_params__[0]
print(parameter.__default__.__name__)
print(make_value(42))

The default is metadata on the type parameter; it does not convert the argument to int at runtime.

The other type-parameter kinds

Python also provides TypeVarTuple with a leading * and ParamSpec with a leading **. These support generic APIs involving a variable number of types or a callable’s parameter list. They are useful, but their practical typing patterns are more advanced than the single-type-parameter examples in this lesson.

python other-parameter-kinds.py
1
2
3
4
type Record[*Ts] = tuple[*Ts]

record: Record[int, str, float] = (7, "Ada", 3.14)
print(record)

A TypeVarTuple can represent a variable-length sequence of type parameters.

python paramspec-alias.py
1
2
3
4
5
6
7
8
9
from collections.abc import Callable

type Handler[**P, R] = Callable[P, R]

def name_length(name: str) -> int:
    return len(name)

handler: Handler[[str], int] = name_length
print(handler("Ada"))

A ParamSpec can describe the parameter list of a callable while another type parameter describes its result.

Key idea

A type parameter list makes a function, class, or type alias generic. Start with a simple T when one type varies, add more parameters when types have different roles, and move to TypeVarTuple or ParamSpec only when the API genuinely needs their more advanced relationships.

Was this page helpful?

Mark this learning resource complete to track your learning progress.