首页 > 编程问答 >Python 协议和 Django 模型

Python 协议和 Django 模型

时间:2024-07-23 12:03:42浏览次数:15  
标签:python django python-typing

假设我有一个简单的协议 A 和一个未能实现该协议的类 B

from typing import Protocol

class A(Protocol):
    def foo(self) -> str: ...

class B:
    pass

当下面的代码进行类型检查时,Mypy 将正确地抱怨

x: A = B()
mypy .
error: Incompatible types in assignment (expression has type "B", variable has type "A") 

如果我有它,那么 B 继承from django.db.models.Model 如下

class A(Protocol):
    def foo(self) -> str: ...

class B(models.Model):
    pass


x: A = B()

mypy 在检查行 x: A = B() 时不再抛出错误。我本以为它会抛出错误,因为 B 没有(看似)实现 A 协议。我认为这一定与 models.Model 的一些继承属性有关,这使得mypy认为 foo 方法在那里。

有谁知道这里发生了什么,并就如何让mypy检查是否或有建议Django 模型没有实现协议吗?请注意,我希望模型不需要显式地对协议进行子类化。


观察到的行为是由于 django.db.models.Model 与 mypy 的交互方式造成的,尤其是在处理与运行时类型信息相关的类型提示时。以下是发生情况的简要细分:

  1. Django 模型的动态性质: Django 模型旨在具有高度动态性。在运行时,根据在模型定义中定义的字段以及从数据库架构中推断的其他信息,将向它们添加属性(例如,数据库列)。

  2. mypy 和运行时信息: mypy 主要是一个静态类型检查器。虽然它在处理某些类型的运行时信息方面做得越来越好,但它不能总是完全模拟 Django 模型在运行时发生的动态属性添加。

  3. 推断和 Any 当 mypy 遇到它没有足够类型信息的情况(例如,动态添加的 Django 模型属性)时,它通常会默认使用 Any 类型。 Any 类型实质上是一个万能类型,这意味着 mypy 不会对其进行任何类型检查。

在的情况下,即使 B 没有显式实现 foo ,mypy 也会看到 B 继承自 models.Model 。由于 Django 模型的动态性质,mypy 不能确定 B 在运行时是否 不会 具有 foo 方法(它可能来自数据库字段或其他机制)。因此,为了安全起见,mypy 不再引发错误,因为它假设可能存在在静态分析期间无法检测到的 foo 实现。

使 mypy 更严格

可以通过禁用 ignore_missing_imports 并为 Django 使用插件来提高 mypy 的严格性。这将有助于 mypy 更准确地理解 Django 模型,并可能在缺少的协议方法上提供更准确的类型检查。

解决方法

可以考虑以下解决方法来解决此问题:

  • 显式协议实现: 最明确的解决方案是修改的模型以显式实现协议: python class B(models.Model): def foo(self) -> str: return "This is foo from B"

  • 自定义类型提示: 如果由于某种原因无法修改模型,可以创建更精确的自定义类型提示来描述的模型,从而有可能在静态分析期间捕获不兼容性。

  • 运行时检查: 作为最后的手段,可以使用运行时检查来验证的模型是否符合协议: ```python from typing import TypeGuard

    @TypeGuard def implements_a(obj: object) -> TypeGuard[A]: return hasattr(obj, 'foo') and callable(obj.foo)

    def my_function(a_instance: A): ...

    b_instance = B() if implements_a(b_instance): my_function(b_instance) # Type safe within the if block ```

请记住,Django 模型的动态性本质上使其在静态类型检查方面具有挑战性。使用上述方法可以找到一个平衡点,该平衡点在保持代码库类型安全的期望水平与 Django 提供的灵活性和动态性之间取得平衡。

标签:python,django,python-typing
From: 77076912

相关文章

  • Python字段的字符串注释
    Python是否支持类字段的任何类型的字符串注释?例如,在Golang中,我可以定义这样的结构,带有可选的字符串标签:typeUserstruct{Namestring`example:"name"`}我需要在Python中定义一个新类,其中包含包含点.的字段名称。我希望可能有某种方法来注释字段,或者......
  • 具有未定义变量和注释的有效 Python 行
    我来了一篇文章,其中包含未定义的变量名称和某些类型注释。该行有效。它有什么作用以及可能的用途是什么?withundefinedvariablenamewithsometypeannotation.Thelineisvalid.Whatitdoesandwhatarepossibleusages?>>>x:int>>>xTraceback(mostr......
  • 使用 Python 通过逻辑应用运行长时间运行的 Azure Functions
    我已经尝试解决这个问题有一段时间了,但我似乎找不到解决方案。因此,正如标题所示,我试图通过函数在逻辑应用程序中运行长时间运行的操作。我有一个python代码,可以比较2个excel文件并进行一些转换。它工作正常,但是,Excel文件包含近20k行(它是动态的,将来会添加更多行),因此......
  • 使用递归函数计算阶乘时,我在 python 中遇到类型错误
    defcalc_fact(n):如果(n==1或n==0):返回1别的:n*calc_fact(n-1)print(calc_fact(5))试图创建函数来计算阶乘,不支持类型错误操作数*:对于int或Nonetype我不知道为什么谢谢Python代码中出现“类型错误:不支持的操作数类型为*:'int'和'NoneType'”表明你......
  • 如何调试 python Flask [84] [CRITICAL] WORKER TIMEOUT?
    调试:gtts.tts:保存到temp.mp37月22日09:10:56PM[2024-07-2215:40:56+0000][84][严重]工作超时(pid:87)|||7月22日09:10:56PM[2024-07-2215:40:56+0000][87][INFO]工人退出(pid:87)7月22日09:10:57PM[2024-07-2215:40:57+0000][95][INF......
  • 类型错误:无法将函数返回值转换为 Python 类型!签名是 () -> 处理 anaconda spider
    这是代码:importosimportrandomimportnumpyasnpimportpandasaspdimporttensorflowastffromtensorflow.kerasimportbackendasKfromtensorflow.keras.layersimportDense,Dropout,Flatten,Conv2D,MaxPool2D,Input......
  • python进阶---闭包与装饰器
    一、闭包        在Python中,闭包是指一个函数内部定义的函数,这个内部函数可以访问并修改其外部函数的局部变量,即使外部函数已经执行完毕。闭包可以通过多层函数嵌套来实现。    闭包的三要素:    1、外部函数嵌套内部函数    2、外部函数返......
  • 强制从当前包自动导入的 Python 以此包的名称为前缀
    我在VSCode中使用Python和Pylance扩展。在我正在编辑的自己的包中,自动添加的导入(设置“导入格式:绝对”)如下所示:frommydirectory.myfileimportmyclass但是,我的Python包正在被被一个(非常愚蠢且不可协商的)外部系统消耗,该系统拒绝正确解释它,除非导入的格式特别......
  • Python语言-面向对象
    知识代码classJobSalary(object):job=''def__init__(self,city):self.jobname="数据分析师"self.exp=''self.city=city#方法defdata_normalize(self,data):print(f'正在规范化......
  • 需要帮助使用 Selenium Python 单击 Microsoft Teams 按钮
    我将Python与Selenium结合使用,并自动登录MicrosoftTeams。进入后,弹出窗口显示我需要单击“立即切换”以切换到V2版本。我似乎无法使用SeleniumPython成功单击此按钮。谁能帮我自动点击这个按钮?这是我不成功的尝试:self.driver.find_element(By.CLASS_NAME,......