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

问题描述 投票:0回答:1

我有以下类型注释的 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

为什么第一个例子不起作用?我真的更喜欢使用它,因为第二个示例不会以任何方式将返回值与

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

python django django-models mypy python-typing
1个回答
2
投票

为我的项目设置 django-stubs 后,第一个示例可以正常工作。可以从这个SO答案找到很好的说明。

最后一个示例正确地给出了 mypy 的错误,因为 Python 不允许这样做。引用我打开的GitHub问题

objects 属性可以包含任意

Model
子类的实例,但
get_obj_or_none
的返回类型包含
T
的特定子类型
Model
Type[T]
在此示例中没有任何效果,因为
objects
属性的类型在子类中是相同的(它不使用“self”类型)。我认为没有办法将 self 类型用于类变量。

© www.soinside.com 2019 - 2024. All rights reserved.