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; ?>
】
相关推荐
- Win10怎么卸载迅雷_Win10彻底卸载迅雷方法
- c++如何利用doxygen生成开发文档_c++
- Windows10如何更改开机密码_Win10登录
- Windows10电脑怎么设置电源按钮_Win10
- 如何在Golang中实现微服务负载均衡_Golan
- PHP接收参数值为空怎么办_判断和处理空参数方法说
- 如何在Golang中实现并发消息队列消费者_Gol
- 如何在Golang中解压文件_Golang com
- 如何在 Go 中正确初始化结构体中的 map 字段
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- 网站内页做seo排名怎么做?
- Win11怎么连接蓝牙耳机_Win11蓝牙设备配对
- Win11怎么设置快速访问_Windows11文件
- Windows 10怎么把任务栏放在屏幕上方_Wi
- php增删改查在php8里有什么变化_新特性对cu
- 作用域操作符会影响性能吗_php静态调用性能分析【
- Python数据挖掘进阶教程_分类回归与聚类案例解
- 如何优化Golang Web性能_Golang H
- Python装饰器设计思路_功能增强机制说明【指导
- C#如何在一个XML文件中查找并替换文本内容
- Python包结构设计_大型项目组织解析【指导】
- GML (Geography Markup Lan
- Mac上的iMovie如何剪辑视频?(新手入门教程
- 如何在Golang中编写异步函数测试_Golang
- C++友元类使用场景_C++类间协作设计方式讲解
- 如何用::实现工具类方法调用_php静态工具类设计
- php修改数据怎么改富文本_update更新htm
- Go 中实现 Python urllib.quot
- MAC的“接续互通”功能无法使用怎么办_MAC检查
- 为什么Go建议使用error接口作为错误返回_Go
- Python脚本参数接收_sys与argparse
- php怎么操作Redis_Redis扩展连接与基本
- Win11怎么更改默认打开方式_Win11关联文件
- Win11怎么开启游戏工具栏_Windows11
- php内存溢出怎么排查_php内存限制调试与优化方
- Win11快速助手怎么用_Win11远程协助连接教
- LINUX怎么进行文本内容搜索_Linux gre
- php485函数执行慢怎么优化_php485性能提
- Win11怎样安装微信开发者工具_Win11安装开
- c++怎么使用类型萃取type_traits_c+
- Win11怎么查看wifi信号强度_检测Windo
- Mac如何使用听写功能_Mac语音输入打字【效率技
- c# F# 的 MailboxProcessor
- Win10怎样卸载DockerDesktop_Wi
- Windows执行文件被SmartScreen拦截
- Win11如何设置开机自动联网 Win11宽带连接
- Win11如何更新显卡驱动 Win11检查和安装设
- Win11怎么关闭透明效果_Windows11辅助
- Win11怎样安装剪映专业版_Win11安装剪映教
- C++ STL算法库怎么用?C++常用算法函数(s

QQ客服