Python *args 和 **kwargs 的底层原理
技术百科
冰川箭仙
发布时间:2026-01-26
浏览: 次 Python函数调用中args和*kwargs不是语法糖,而是解释器在字节码层面硬编码的机制:CALL_FUNCTION_EX指令直接处理栈顶元组和字典,co_flags标记形参特性,类型检查严格限定为tuple和dict,装饰器需同时使用二者以完整转发参数,性能开销主要来自类型检查、字典合并及对象新建。
Python 函数调用时参数如何被拆包和收集
根本不是语法糖,而是解释器在字节码层面硬编码的约定。CALL_FUNCTION_EX 指令专门处理 *args 和 **kwargs,它把栈顶的

def f(*a, **k),CPython 会生成特殊标记的 co_flags(比如 CO_VARARGS | CO_VARKEYWORDS),让运行时知道该从哪取值。
这意味着:*args 不是“把列表转成参数”,而是“把栈上已有的元组整体当位置参数传入”;**kwargs 同理,不是“字典展开”,而是“把栈上字典对象直接绑定为 **k 所指的映射”。
为什么 *args 必须是 tuple、**kwargs 必须是 dict
因为解释器只认这两种类型——源码里 ceval.c 的 call_function_ex 分支明确检查:
- 如果传了非
tuple给*args,抛TypeError: argument after * must be an iterable, not X - 如果传了非
dict给**kwargs,抛TypeError: argument after ** must be a mapping, not X
注意:这里的“必须是 tuple”指调用侧传入的实参类型,不是形参名 a 在函数体内不能被修改。函数体内拿到的 a 确实是 tuple,但你可以把它转成 list 再操作。
*args 和 **kwargs 在装饰器里为什么总要一起出现
因为装饰器要完整转发原始调用,而你无法预知被装饰函数需要什么参数形式。漏掉任意一个,就会破坏调用契约:
- 只写
def deco(f): def wrapper(*args): return f(*args)→ 原函数若带关键字参数,全丢进*args,触发TypeError - 只写
def wrapper(**kwargs)→ 所有位置参数丢失,同样报错 - 正确写法必须是
def wrapper(*args, **kwargs): return f(*args, **kwargs)
这也是为什么 functools.wraps 要重写 __signature__:它得模拟出原函数的参数结构,否则 IDE 和 inspect.signature() 看到的永远只是 (*args, **kwargs)。
性能开销在哪?哪些场景能省掉它们
主要开销在调用时的两次对象检查 + 一次字典合并(**kwargs),以及函数帧内多分配两个局部变量。不常被注意的点是:
- 每次调用都新建
tuple和dict—— 即使你传的是空*[]和**{},也会触发构造 - 用
functools.partial预绑定了参数的函数,底层仍走*args/**kwargs路径,没省掉检查 - 如果确定函数只接收固定参数,就别用
*args/**kwargs做兜底;高频路径上(如事件循环回调)应显式声明参数,避免无谓的元组/字典创建
最易被忽略的是:*args 收集后是不可变 tuple,想改就得转 list;而 **kwargs 是可变 dict,但修改它不会影响调用方传入的原字典——这是浅拷贝行为,不是引用传递。
# 的是
# 就会
# 这是
# 把它
# 也会
# python
# 传了
# app
# 两次
# word
# 循环
# 对象
# 实参
# 编码
# 字节
# 为什么
# 栈
# 事件
# 体内
# 局部变量
# ide
# 转成
# python函数
# 形参
# 引用传递
# 只写
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么设置组合键快捷方式_Windows1
- MAC如何设置网卡MAC地址克隆_MAC终端修改物
- C#如何使用Channel C#通道实现异步通信
- php后缀怎么变mp4能播放_让php伪装mp4正
- Win11怎么更改文件夹图标_自定义Win11文件
- c++怎么实现高并发下的无锁队列_c++ std:
- 如何使用Golang sync.Map实现并发安全
- MySQL 中使用 IF 和 CASE 实现查询字
- MAC怎么用连续互通相机里的“桌上视角”_MAC在
- Win11文件夹预览图不显示怎么办_Win11缩略
- php怎么捕获异常_trycatch结构处理运行时
- Windows的便笺功能如何使用?(桌面备忘技巧)
- Win11讲述人怎么关闭_Win11误触开启语音朗
- Win11任务栏怎么放到顶部_Win11修改任务栏
- Python配置文件操作教程_JSONINIYAM
- 如何自定义Windows终端的默认配置文件?(Po
- php485在macos下怎么配置_php485
- Mac自带的词典App怎么用_Mac添加和使用多语
- 如何在Golang中处理模块冲突_解决依赖版本不兼
- XML的“混合内容”是什么 怎么用DTD或XSD定
- Win11怎么关闭定位服务_保护Win11位置隐私
- Win11怎么设置麦克风权限_允许应用访问Win1
- Win10怎样清理C盘阿里旺旺缓存_Win10清理
- Windows驱动无法加载错误解决方法_驱动签名验
- php本地部署后session无法保存_sessi
- php删除数据怎么软删除_添加is_del字段标记
- 如何在 Pandas 中按元素交集合并两列字符串
- 如何使用Golang操作指针变量_Golang解引
- Win11应用商店下载慢怎么办 Win11更改DN
- Python集合操作技巧_高效去重解析【教程】
- c++怎么使用std::unique实现去重_c+
- Windows 10怎么把任务栏放在屏幕上方_Wi
- 如何高效识别并拦截拼接式恶意域名 spam
- Drupal 中 HTML 链接被双重转义导致渲染
- Win11关机快捷键是什么_Win11快速关机方法
- 如何用正则表达式精确匹配“start”到“end”
- Win11怎么连接蓝牙耳机_Win11蓝牙设备配对
- Windows10系统服务优化指南_Win10禁用
- Win11怎么设置虚拟键盘_打开Win11屏幕键盘
- php485读数据时阻塞怎么办_php485非阻塞
- 如何使用Golang log设置日志输出格式_Go
- Win10怎样卸载DockerDesktop_Wi
- Win11麦克风没声音怎么设置_Win11麦克风权
- Django 密码修改后会话失效的解决方案
- 如何使用正则表达式批量替换重复的“-”模式为固定字
- Win11键盘快捷键大全_Windows 11常用
- Windows10系统怎么查看设备管理器_Win1
- 如何在Golang中实现服务熔断与限流_Golan
- 如何使用Golang benchmark测量函数延
- 手机php怎么转mp4_手机端php文件转mp4a

QQ客服