Python Type Hints - How to Specify a Class Rather Than an Instance Thereof

Cats & Dogs 4: The Battle of the Types

In a type hint, if we specify a type (class), then we mark the variable as containing an instance of that type. To specify that a variable instead contains a type, we need to use type[Cls] (or the old syntax typing.Type).

For example, take this code:

class Animal:
    def __init__(self, *, name: str):
        self.name = name


class Cat(Animal):
    ...


class Dog(Animal):
    ...


def make_animal(animal_class, name):
    return animal_class(name=name)


animal = make_animal(Dog, "Kevin")

We need to add type hints for make_animal(). How can we specify that its animal_class argument should contain a subclass of Animal?

We could try using Animal:

def make_animal(animal_class: Animal, name: str) -> Animal:
    return animal_class(name=name)

The type of animal_class looks the same as the return type though, even though we know the first is a class and the second an instance. And indeed, Mypy flags some errors:

$ mypy example.py
example.py:15: error: "Animal" not callable
example.py:18: error: Argument 1 to "make_animal" has incompatible type "Type[Dog]"; expected "Animal"
Found 2 errors in 1 file (checked 1 source file)

What we should use instead is type[Animal]:

def make_animal(animal_class: type[Animal], name: str) -> Animal:
    return animal_class(name=name)

This means “animal_class takes the class Animal or any subclass”. Running Mypy on this produces no errors.

The type[] syntax was only added in Python 3.9, thanks to PEP 585. Mypy 0.800+ supports this new syntax on older Python versions if you use from __future__ import annotations.

The previous syntax is typing.Type[Class]:

from typing import Type

...


def make_animal(animal_class: Type[Animal], name: str) -> Animal:
    return animal_class(name=name)

If your code uses typing.Type and you add __future__.annotations or upgrade to Python 3.9+, you can update your old syntax with pyupgrade.

Fin

Dogs clearly beat cats,

—Adam


Improve your Django develompent experience with my new book.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: ,