typing.ParamSpec 如何保留被装饰函数的 *args / **kwargs 类型
发布时间 - 2026-01-31 00:00:00 点击率:次不能——ParamSpec仅记录参数结构“形状”,不保存args/kwargs的具体类型注解,P.args恒为tuple[object, ...],需用Concatenate显式拼接才能保留如args: float等类型信息。
ParamSpec 能不能原样保留 *args 和 **kwargs 的类型?
不能直接保留——ParamSpec 本身不捕获 *args: P.args 或 **kwargs: P.kwargs 的具体类型,它只记录参数结构的“形状”,不保存动态参数的实际注解。如果你写 def f(*args: int, **kwargs: str),P = ParamSpec('P') 绑定后,P.args 是 tuple[object, ...],P.kwargs 是 dict[str, Any],原始 int 和 str 信息就丢了。
用 Concatenate + 显式标注才能保留 *args 类型
要让装饰器把 *args: int 传下去,必须手动拆开参数结构,用 Concatenate 把固定参数和可变参数拼起来,并显式写出 *args 的类型。常见错误是只写 P,结果类型检查器认为 *args 是泛型占位符而非具体类型。
-
ParamSpec适合转发签名但不关心*args/**kwargs具体类型(比如日志装饰器) - 若需保留
*args: int,定义装饰器时得用Callable[Conc,并让被装饰函数显式标注
atenate[int, P], R]
*args: int -
**kwargs同理:用Concatenate[Unpack[T], P](Python 3.12+)或配合TypedDict模拟强类型**kwargs
实际例子:带类型感知的重试装饰器
下面这个装饰器能正确推导 f(x: str, *args: float, **kwargs: bool) 中 *args 是 float、**kwargs 是 bool:
from typing import Callable, TypeVar, ParamSpec, Concatenate, Unpack, TypedDict import timeP = ParamSpec('P') R = TypeVar('R')
假设我们只关心 *args: float,其他保持原样
def retry( func: Callable[Concatenate[float, P], R] ) -> Callable[Concatenate[float, P], R]: def wrapper(*args: float, *kwargs: P.kwargs) -> R: for _ in range(3): try: return func(args, **kwargs) except Exception: time.sleep(1) raise RuntimeError("Failed after retries") return wrapper
使用时必须显式标注 *args / **kwargs 类型
def my_func(x: str, *args: float, **kwargs: bool) -> int: return len(x) + sum(int(a) for a in args)
wrapped = retry(my_func) # ✅ mypy 知道 wrapped 接收 *args: float, **kwargs: bool
为什么 P.args 总是 tuple[object, ...]?
这是 ParamSpec 的设计限制:它抽象的是“调用时参数如何分组”,不是“每个参数的静态类型”。P.args 对应的是 *args 形参整体,而 Python 类型系统中 *args: T 的类型本质是 tuple[T, ...],但 P 不存储这个 T——它只存 tuple[object, ...] 作为占位。真正要恢复 T,只能靠 Concatenate 显式拼接,或用 Callable[[int, str, *tuple[float, ...]], None] 这种硬编码方式。
所以别指望 ParamSpec 自动推导出 *args 的元素类型;它最常被误用的地方,就是以为 P.args 能当 tuple[float, ...] 用。
# python
# 编码
# app
# ai
# 为什么
# Float
# Object
# 可变参数
# bool
# int
# 泛型
# 形参
# 的是
# 它只
# 不保存
# 这是
# 要让
# 而非
# 但不
# 绑定
# 或用
# 你写
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
怎么用AI帮你设计一套个性化的手机App图标?
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
如何在云主机上快速搭建网站?
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
Win11怎么设置默认图片查看器_Windows11照片应用关联设置
中山网站推广排名,中山信息港登录入口?
儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?
Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】
Laravel Blade模板引擎语法_Laravel Blade布局继承用法
用v-html解决Vue.js渲染中html标签不被解析的问题
网站制作软件免费下载安装,有哪些免费下载的软件网站?
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
Python图片处理进阶教程_Pillow滤镜与图像增强
Android利用动画实现背景逐渐变暗
C语言设计一个闪闪的圣诞树
网站制作价目表怎么做,珍爱网婚介费用多少?
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
Laravel如何创建自定义Artisan命令?(代码示例)
Laravel如何操作JSON类型的数据库字段?(Eloquent示例)
制作公司内部网站有哪些,内网如何建网站?
高防服务器租用如何选择配置与防御等级?
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
Swift开发中switch语句值绑定模式
网站制作大概多少钱一个,做一个平台网站大概多少钱?
微信公众帐号开发教程之图文消息全攻略
使用豆包 AI 辅助进行简单网页 HTML 结构设计
Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】
高性价比服务器租赁——企业级配置与24小时运维服务
EditPlus中的正则表达式实战(6)
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
长沙做网站要多少钱,长沙国安网络怎么样?
千库网官网入口推荐 千库网设计创意平台入口
Laravel安装步骤详细教程_Laravel环境搭建指南
JavaScript如何实现类型判断_typeof和instanceof有什么区别
php结合redis实现高并发下的抢购、秒杀功能的实例
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】
如何打造高效商业网站?建站目的决定转化率
Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
在centOS 7安装mysql 5.7的详细教程
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
HTML透明颜色代码怎么让图片透明_给img元素加透明色的技巧【方法】
如何快速重置建站主机并恢复默认配置?


