当前位置:首页 > 文章列表 > 文章 > python教程 > Python get_protocol_members 怎么读取 Protocol 成员集合

Python get_protocol_members 怎么读取 Protocol 成员集合

来源:17golang原创 2026-10-06 13:14:21 0浏览 收藏

在 Python 3.13 及以上版本中,读取 Protocol 的成员集合,直接调用 typing.get_protocol_members(协议类) 即可。它返回 frozenset[str],内容是协议要求的方法名和属性名;如果传入的对象不是协议类,则会抛出 TypeError。

官方文档:https://docs.python.org/3/library/typing.html#typing.get_protocol_members

这个 API 看似只是一个小型反射工具,却能解决插件注册、协议文档生成和接口清单检查中的一个常见故障:手工读取 __dict__ 时,当前类里看得到的名称不等于完整的协议契约。

一、成员探测故障影响了哪些结果

设想一个插件检查器要把协议要求输出成日志。第一版实现遍历 protocol.__dict__,开发环境里的简单协议看起来正常;协议一旦继承另一个协议,生成的清单就漏掉父协议成员。与此同时,__module__、__doc__ 等类内部名称又混进结果,最终让“缺少哪些接口”的提示失去可信度。

from typing import Protocol

class Closable(Protocol):
    def close(self) -> None: ...

class Repository(Closable, Protocol):
    name: str

    def get(self, key: str) -> bytes | None: ...

# 只看当前类命名空间会漏掉继承来的 close,且还要自己过滤内部名称。
guessed = {
    name for name in Repository.__dict__
    if not name.startswith("_")
}
print(guessed)

这里的影响不在类型检查器本身,而在运行时工具:它可能生成不完整的文档、注册错误的适配器,或向用户报告一个并不存在的“接口完整”状态。

二、按复现时间线锁定触发条件

把故障压缩成最小案例后,触发过程很清楚:

  1. 先定义只包含方法的基础协议 Closable。
  2. 让 Repository 继承它,并新增注解属性 name 和方法 get。
  3. 读取 Repository.__dict__,结果只能代表当前类命名空间。
  4. 当工具把这个结果当成完整契约时,继承成员 close 被漏掉。

注解属性又是另一个容易误判的点。name: str 主要记录在注解信息里,不应要求它像普通赋值属性那样出现在类字典的同一位置。自己拼接 __dict__ 与 __annotations__,还要持续跟进 typing 内部排除名单,维护成本会不断上升。

三、根因是把类命名空间当成协议契约

Protocol 描述的是结构化类型契约。根据 PEP 544,协议成员可以是方法,也可以是变量;一个具体类型是否满足协议,还要由静态类型检查器判断成员类型和方法签名是否兼容。类的 __dict__ 只是某一层命名空间,不承担“给出完整协议成员集合”的公开承诺。

Protocol 类命名空间、继承成员与完整协议契约之间的根因关系图
图 1:重点查看“错误输入”和“完整契约”两个区域。只读取当前类命名空间会漏掉父协议成员,而公开反射 API 会合并协议需要的成员名称;这是静态根因说明图,不是运行截图。

因此,故障并不是“过滤条件少写了一个名称”,而是选择了错误的数据入口。继续扩充私有过滤列表,只会把偶发错误变成长期兼容负担。

四、改用 get_protocol_members 完成修复

Python 3.13 为这个任务加入了公开函数 typing.get_protocol_members。修复后的代码只需要把协议类传进去:

from typing import Protocol, get_protocol_members

class Closable(Protocol):
    def close(self) -> None: ...

class Repository(Closable, Protocol):
    name: str

    def get(self, key: str) -> bytes | None: ...

members = get_protocol_members(Repository)

print(members)
print(sorted(members))

成员集合包含 close、get 和 name。函数返回的是不可变的 frozenset,集合没有业务顺序;如果要写日志、生成 JSON 或做快照测试,应先用 sorted() 得到稳定列表。

frozenset({'close', 'get', 'name'})
['close', 'get', 'name']

集合打印时的排列可能不同,这不代表成员变化。判断某个名称是否存在时直接使用集合成员测试即可:

required = get_protocol_members(Repository)

if "close" in required:
    print("Repository 要求 close 成员")

五、继承与泛型场景怎么读取

协议继承正是这个 API 比手工反射可靠的地方。返回集合会覆盖协议类及其协议父类形成的成员要求,所以前例中的 close 不会丢失。

泛型协议需要注意传参对象。get_protocol_members 接受的是协议类;参数化后的 Store[int] 是泛型别名,不是要传给该函数的协议类。读取成员名称时传原始协议类 Store:

from typing import Protocol, TypeVar, get_protocol_members

T = TypeVar("T")

class Store(Protocol[T]):
    def save(self, value: T) -> None: ...

print(sorted(get_protocol_members(Store)))

# 不要传 Store[int];它是参数化泛型别名,不是协议类本身。
# get_protocol_members(Store[int])  # TypeError

另一个边界是普通类。即便普通类恰好实现了 save,它也不是 Protocol 定义,传给函数仍会抛出 TypeError。需要接受不确定输入时,可以先用同在 Python 3.13 新增的 typing.is_protocol 判断。

from typing import get_protocol_members, is_protocol

def protocol_member_names(candidate: object) -> list[str]:
    if not is_protocol(candidate):
        raise ValueError(f"需要 Protocol 类,收到 {candidate!r}")

    return sorted(get_protocol_members(candidate))
is_protocol 判定、get_protocol_members 读取与稳定输出之间的修复流程关系图
图 2:从“输入判定”进入“公开 API”,Protocol 类得到不可变成员集合并排序输出;普通类和参数化泛型别名进入明确错误分支。这是静态修复关系图,不是程序运行截图。

六、旧版本 Python 怎么保持同一接口

typing.get_protocol_members 从 Python 3.13 开始进入标准库。需要兼容更早 Python 的项目,可以使用 typing_extensions;该项目从 4.7.0 起提供同名回移实现,而且能处理由 typing.Protocol 或 typing_extensions.Protocol 定义的协议。

try:
    # Python 3.13 及以上优先使用标准库。
    from typing import Protocol, get_protocol_members, is_protocol
except ImportError:
    # 旧版 Python 使用 typing_extensions 4.7.0 及以上。
    from typing_extensions import Protocol, get_protocol_members, is_protocol

项目依赖应显式声明最低版本,而不是直接读取 _is_protocol、__protocol_attrs__ 等私有实现细节。公开函数的价值就在于让版本差异由标准库或兼容包处理。

七、加入防复发检查与适用边界

修复成员清单后,还要避免把这个函数扩展成它没有承诺的“运行时类型检查器”。下面几条可以作为防复发检查:

  • 先判定输入:来源不可信时先调用 is_protocol,再读取成员。
  • 固定输出顺序:日志、文档和快照测试统一使用 sorted(),不要依赖集合展示顺序。
  • 传协议类:传 Store,不要传实例、普通实现类或 Store[int] 这样的泛型别名。
  • 不要读取私有属性:不要把 __protocol_attrs__ 当作稳定业务接口。
  • 不要误判兼容性:get_protocol_members 只返回名称,不比较实现类的方法签名与注解类型。

如果目标是检查一个对象能否在运行时满足协议,可以在协议上使用 @runtime_checkable 后调用 isinstance();但官方文档明确提醒,这类运行时检查只关注成员是否存在,不校验类型签名。需要严格验证兼容性时,仍应把 mypy、Pyright 等静态类型检查器放进开发或 CI 流程。

返回值为什么是 frozenset 而不是 list?

成员集合表达的是“有哪些名称”,不是声明顺序。frozenset 不可变,适合做包含判断和集合比较;需要可重复展示时再转换成排序列表。

它会返回父 Protocol 的成员吗?

会。协议成员在创建协议类时按其继承关系收集,因此读取子协议时能得到自身成员与协议父类带来的成员要求。

能用它判断某个类是否完整实现 Protocol 吗?

不能仅靠这个函数。它读取的是协议要求的名称,不检查另一个类,也不比较参数、返回值和属性类型。静态兼容性应由类型检查器判断;简单运行时存在性检查可结合 @runtime_checkable,但仍不等于签名验证。

归纳起来,读取协议成员的稳定写法是:先确认传入的是未参数化的 Protocol 类,再调用 get_protocol_members,展示时排序。这样既能保留继承成员和注解属性,也能避开 typing 私有实现变化带来的维护风险。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 事务里的查询为什么不能再使用原来的 DB 句柄Go 事务里的查询为什么不能再使用原来的 DB 句柄
上一篇
Go 事务里的查询为什么不能再使用原来的 DB 句柄
青漫漫画加载很快吗?边缘分发节点、网络环境与体验边界
下一篇
青漫漫画加载很快吗?边缘分发节点、网络环境与体验边界
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    346次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    408次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    407次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    369次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    191次使用