我有以下类型注释的 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 在这两个例子中表现不同?我可以以某种方式解决这两种情况吗?
为我的项目设置 django-stubs 后,第一个示例可以正常工作。可以从这个SO答案找到很好的说明。
最后一个示例正确地给出了 mypy 的错误,因为 Python 不允许这样做。引用我打开的GitHub问题:
objects 属性可以包含任意
子类的实例,但Model
的返回类型包含get_obj_or_none
的特定子类型T
。Model
在此示例中没有任何效果,因为Type[T]
属性的类型在子类中是相同的(它不使用“self”类型)。我认为没有办法将 self 类型用于类变量。objects