Как правильно использовать T = TypeVar ('T', bound = ...) с Type [T]? - PullRequest
1 голос
/ 31 января 2020

У меня есть следующий аннотированный тип Django код:

from typing import Optional, Type, TypeVar

from django.db import models

T = TypeVar('T', bound=models.Model)


def get_obj_or_none(model: Type[T], obj_id: int) -> Optional[T]:
    try:
        return model.objects.get(pk=obj_id)
    except model.DoesNotExist:
        return None

Функция ожидает класс, полученный из django.db.models.Model в качестве первого параметра и int id в качестве второго параметра:

# obj is now the Person with id 123, or None if that didn't exist.
obj = get_obj_or_none(Person, 123)

Но когда я запускаю mypy в коде, я получаю сообщение об ошибке:

error: "Type[T]" has no attribute "objects"
error: "Type[T]" has no attribute "DoesNotExist"

Но если изменить код на этот и снова запустить mypy, я не получите ошибок:

from typing import Optional, Type

from django.db import models


def get_obj_or_none(model: Type[models.Model], obj_id: int) -> Optional[models.Model]:
    try:
        return model.objects.get(pk=obj_id)
    except model.DoesNotExist:
        return None

Почему не работает первый пример? Я действительно предпочел бы использовать его, так как второй пример никак не возвращает ie возвращаемое значение параметру model, поэтому функция может возвращать экземпляр, который полностью не связан с классом, заданным как первый параметр.

Я использую Python 3.8.1 с mypy 0,761.


Редактировать :

Вот самодостаточный Пример, который можно проверить следующим образом:

from typing import Dict, Optional, Type, TypeVar


class Model:
    objects: Dict[int, 'Model'] = {}


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


def get_obj_or_none(model: Type[T], obj_id: int) -> Optional[T]:
    try:
        return model.objects[obj_id]
    except KeyError:
        return None

Запуск mypy на этом дает (к моему удивлению) совершенно другую ошибку:

type_example.py:17: error: Incompatible return value type (got "Model", expected "Optional[T]")
Found 1 error in 1 file (checked 1 source file)

Почему mypy ведут себя по-разному на этих двух примерах? Можно ли как-то исправить оба случая?

1 Ответ

0 голосов
/ 04 февраля 2020

Первый пример работает правильно после настройки django -stubs для моего проекта. Хорошие инструкции можно найти в этом SO-ответе .

. В последнем примере правильно выдается ошибка от mypy, поскольку она не разрешена Python. Перефразируя из проблему GitHub , которую я открыл:

Атрибут objects может содержать экземпляр произвольного подкласса Model, но тип возвращаемого значения get_obj_or_none содержит спецификацию c подтип T из Model. Type[T] не имеет никакого эффекта в этом примере, так как тип атрибута objects одинаков в подклассах (он не использует тип "self"). Я не думаю, что есть способ использовать собственный тип для переменной класса.

...