本文深入探讨了Python中嵌套协议(Nested Protocols)在类型检查工具Mypy和Pylance中的行为。我们发现,当内部协议作为嵌套类实现时,Mypy/Pylance可能无法正确检测类型不匹配。文章解释了这一现象是Mypy的一个已知限制,并对比了Pyright在此场景下的正确行为,同时提供了Mypy用户通过外部定义类型并赋值的有效规避方案。
在Python的类型提示系统中,Protocol 提供了一种强大的方式来实现结构化子类型(structural subtyping),允许我们定义一个类型必须具备哪些属性和方法,而无需显式继承。这在构建灵活的接口和组件时非常有用。然而,当协议内部包含另一个协议(即嵌套协议)时,其类型检查行为在不同的工具中可能存在差异,尤其是在Mypy和Pylance中。
考虑以下场景:我们定义了一个 Parent 协议,它要求一个名为 Child 的属性,而这个 Child 属性本身又是一个 Child 协议的实例,该 Child 协议要求一个 name 字符串属性。
from typing import Protocol
class Child(Protocol):
name: str
class Parent(Protocol):
Child: Child
# 尝试实现 Parent 协议
class FooBar(Parent):
class Child:
# 这里缺少 name 属性
pass在这个例子中,FooBar 类内部定义了一个名为 Child 的嵌套类。根据 Parent 协议的定义,FooBar.Child 应该符合 Child 协议,即它必须有一个 name: str 属性。然而,在上述实现中,FooBar.Child 并没有 name 属性。直观上,我们期望类型检查器能报告一个错误。
令人惊讶的是,在使用Mypy或Pylance(基于Pyright,但Pylance的某些版本或配置可能与Pyright行为不完全一致)进行类型检查时,上述代码可能不会报告任何错误。这表明Mypy/Pylance在处理这种“作为嵌套类实现的嵌套协议”的场景时,未能正确地推断并检查内部协议的类型合规性。
这并非Mypy的缺陷,而是其内部实现的一个已知限制。Mypy社区已经记录了相关问题(例如GitHub上的issue #14767),表明这种对嵌套类属性的协议检查尚未完全实现。
值得注意的是,另一个流行的Python类型检查器Pyright(VS Code的Pylance扩展底层就是基于Pyright,但其默认配置或版本可能有所不同)在处理此问题时表现得更为严格和准确。Pyright能够正确地识别出 FooBar.Child 违反了 Child 协议,因为它缺少 name 属性,从而报告相应的类型错误。这表明在某些复杂的类型检查场景下,Pyright可能提供更全面的类型安全保障。
尽管Mypy存在上述限制,但我们可以通过调整代码结构来规避这一问题,使其能够正确地检查嵌套协议。核心思想是避免将内部协议的实现直接作为嵌套类定义,而是将其定义为外部类,然后作为属性赋值给实现类。
以下是Mypy能够正确检测错误的修改方案:
from typing import Protocol class Child(Protocol): name: str class Parent(Protocol): Child: Child # 将 Child 的实现定义为外部类 class _ChildImpl: pass # 仍然缺少 name 属性 class FooBar(Parent): # 将外部定义的类赋值给 Child 属性 Child = _ChildImpl
在这种修改后的代码中,Mypy会报告以下错误:
E: Incompatible types in assignment (expression has type "type[_ChildImpl]", base class "Parent" defined the type as "Child") [assignment]
错误分析:
Mypy现在能够识别到 _ChildImpl 类型与 Parent 协议期望的 Child 协议不兼容,因为它缺少 name 属性。这是因为当 Child 被赋值为一个外部类型时,Mypy能够更有效地对其进行类型推断和协议检查。
正确的实现方式:
为了完全符合协议,_ChildImpl 应该包含 name 属性:
from typing import Protocol
class Child(Protocol):
name: str
class Parent(Protocol):
Child: Child
# 正确实现 Child 协议的外部类
class _ChildImpl:
name: str = "default_name" # 提供 name 属性
class FooBar(Parent):
Child = _ChildImpl # 现在类型检查通过理解这些限制和规避方案,有助于我们在使用Python的类型提示系统时,构建更健壮、更易于维护的代码。
# python
# git
# github
# 工具
# vs code
相关文章:
建站之星安装需要哪些步骤及注意事项?
如何在VPS电脑上快速搭建网站?
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
导航网站建站方案与优化指南:一站式高效搭建技巧解析
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
,在苏州找工作,上哪个网站比较好?
高性能网站服务器部署指南:稳定运行与安全配置优化方案
建站之星收费标准详解:套餐费用及年费价格表一览
如何在云虚拟主机上快速搭建个人网站?
在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?
如何快速完成中国万网建站详细流程?
如何在万网开始建站?分步指南解析
建站之星3.0如何解决常见操作问题?
上海网站制作开发公司,上海买房比较好的网站有哪些?
建站之星如何快速更换网站模板?
正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好?
简单实现Android文件上传
济南企业网站制作公司,济南社保单位网上缴费步骤?
魔毅自助建站系统:模板定制与SEO优化一键生成指南
建站之家VIP精选网站模板与SEO优化教程整合指南
PHP 500报错的快速解决方法
高防网站服务器:DDoS防御与BGP线路的AI智能防护方案
广州建站公司哪家好?十大优质服务商推荐
如何快速搭建个人网站并优化SEO?
无锡营销型网站制作公司,无锡网选车牌流程?
如何通过虚拟主机空间快速建站?
陕西网站制作公司有哪些,陕西凌云电器有限公司官网?
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
建站主机选购指南:核心配置优化与品牌推荐方案
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
如何通过IIS搭建网站并配置访问权限?
建站主机SSH密钥生成步骤及常见问题解答?
小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建
,购物网站怎么盈利呢?
如何通过网站建站时间优化SEO与用户体验?
,巨量百应是干嘛的?
黑客如何通过漏洞一步步攻陷网站服务器?
云南网站制作公司有哪些,云南最好的招聘网站是哪个?
如何在Tomcat中配置并部署网站项目?
如何制作网站标识牌,动态网站如何制作(教程)?
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
宝塔建站后网页无法访问如何解决?
建站主机如何选?高性价比方案全解析
宝华建站服务条款解析:五站合一功能与SEO优化设置指南
如何在宝塔面板中创建新站点?
如何在阿里云服务器自主搭建网站?
浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
建站之星安装后如何配置SEO及设计样式?
*请认真填写需求信息,我们会在24小时内与您取得联系。