如何正确使用 T=TypeVar('T', bound=...) 和 Type[T] ?

How to use T=TypeVar('T', bound=...) with Type[T] correctly?

我有以下带类型注释的 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 派生的 class 作为第一个参数,一个 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

为什么第一个示例不起作用?我真的更喜欢使用它,因为第二个示例没有以任何方式将 return 值绑定到 model 参数,因此该函数可能是 returning 一个实例,即与作为第一个参数给出的 class 完全无关。

我正在使用 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 在这两个例子中表现不同?我能以某种方式解决这两种情况吗?

第一个示例在设置 django-stubs for my project. Good instructions can be found from 后正常工作。

最后一个示例正确给出了 mypy 的错误,因为 Python 不允许这样做。套用 a GitHub issue 我打开了:

The objects attribute can contain an instance of an arbitrary Model subclass, but the return type of get_obj_or_none contains a specific subtype T of Model. Type[T] has no effect in this example, since the type of the objects attribute is the same in subclasses (it doesn't use a "self" type). I don't think that there's a way to use a self type for a class variable.