shevron
shevron

Reputation: 3673

Python: typing a generic function that receives a type and returns an instance of that type

I want to add typing to a Python function that takes in a type as an argument (actually, a subtype of a specific class), and return an instance of that type. Think a factory that takes in the specific type as an argument, e.g.:

T = TypeVar('T', bound=Animal)

def make_animal(animal_type: Type[T]) -> T:  # <-- what should `Type[T]` be?
    return animal_type()

(obviously this is a very simplistic example, but it demonstrates the case)

This feels like something that should be possible, but I couldn't find how to properly type-hint this.

Upvotes: 11

Views: 5877

Answers (4)

Unmitigated
Unmitigated

Reputation: 89472

Since Python 3.12, generic function declarations can be simplified with type parameter lists, which obviates the need to explicitly define TypeVars. (Also note that typing.Type was deprecated in Python 3.9 in favor of directly subscripting type.)

def make_animal[T](animal_type: type[T]) -> T:
    return animal_type()

Upvotes: 2

Kyle Tennison
Kyle Tennison

Reputation: 3

You cast objects with typing.cast

from typing import TypeVar, cast

T = TypeVar("T", str, dict)
def my_function(expected_type: type[T]) -> T:

    if expected_type is str:
        return cast(T, "hi")
    else:
        return cast(T, {"message": "hi"})

This is useful if your situation requires dynamic typing that you cannot verify through Python's static typing system.

Upvotes: 0

Zecong Hu
Zecong Hu

Reputation: 3224

Not sure what your question is, the code you posted is perfectly valid Python code. There is typing.Type that does exactly what you want:

from typing import Type, TypeVar

class Animal: ...
class Snake(Animal): ...

T = TypeVar('T', bound=Animal)

def make_animal(animal_type: Type[T]) -> T:
    return animal_type()

reveal_type(make_animal(Animal))  # Revealed type is 'main.Animal*'
reveal_type(make_animal(Snake))   # Revealed type is 'main.Snake*'

See mypy output on mypy-play.

Upvotes: 13

Gijs Wobben
Gijs Wobben

Reputation: 2085

How about something like this?

from __future__ import annotations
from typing import Type


class Animal:
    ...


def make_animal(animal_type: Type[Animal]) -> Animal:
    return animal_type()

Upvotes: -2

Related Questions