Python 关键字专属参数:用 `*` 强制剩余参数必须具名传递

发布时间:2026/10/8 14:10:27
Python 关键字专属参数:用 `*` 强制剩余参数必须具名传递 文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载当你在 Python 函数定义中使用位置参数收集器positional argument collector即裸*时*之后的所有参数在调用时都必须显式具名无论你用的是收集为命名元组的*rest形式还是空的/哑元*形式。这是一条非常实用的接口设计约束——本文以 TIL 仓库中 force-remaining-arguments-to-be-named 一文的实战笔记为核心结合仓库中与之呼应的多篇 Python 笔记strictly-separate-positional-and-keyword-arguments、configure-other-attributes-of-dataclass-field、another-way-to-mark-keyword-only-dataclass-fields讲透 keyword-only arguments 的语法机制、CLI 配置场景下的真实用法、与/标记的组合、以及在dataclass中的对应实现。读完后你将能熟练用*强制配置项尤其是布尔开关在调用点必须具名从根本上杜绝f(True, x)这类魔法数字式调用。核心机制*是位置参数收集器也是关键字专属边界Python 函数定义中*单独出现的位置有一个双重作用它作为位置参数收集器*之前的所有参数只能按位置传递*之后的所有参数只能按关键字传递它在签名中划出一条强制边界*之后声明的每一个参数都是keyword-only arguments关键字专属参数调用时必须写成参数名值的形式。无论你写的是带变量名的收集形式*rest把多余的位置实参收集进一个元组还是裸*不收集任何东西纯粹充当边界标记这条之后必须具名的规则都同样生效。这也是强制剩余参数具名这一技巧的全部原理。实战场景一CLI 工具中用*强制配置项具名这一技巧最常见的落地场景就是把构造函数的可选配置参数强制为具名传参。原作者在py-vmt项目一个基于 Click 的命令行工具中就利用它来强制CliContext的配置值通常是布尔值在调用点必须显式写出参数名class CliContext: def __init__(self, *, verbose: bool, repo: SessionRepository | None None) - None: self.verbose: bool verbose self.active_session: Session | None None self.repo: SessionRepository repo or JsonRepository() self.active_session self.repo.active_session()注意self之后紧跟着一个*这意味着verbose和repo这两个参数都必须是具名参数。对应的调用点是这样的ctx.obj CliContext(verboseTrue)初始化CliContext时你没有任何机会传入一个不带名字的True——它必须与verbose配对出现。上面省略了repo因为它有默认值None会回退到JsonRepository()如果显式传入repo同样必须具名ctx.obj CliContext(verboseTrue, repoPostgresRepository())为什么配置类尤其需要*消除魔法值CliContext(True)没人能看懂True是什么CliContext(verboseTrue)自文档化。防止参数错位当构造函数有多个布尔或同类型参数时位置传参极易写反顺序具名传参在编译/运行前就能被识别。保护默认值可选的配置项如这里的repo放在*之后调用方省略它也不会影响其他参数的定位。注意这里verbose: bool没有默认值是必填的具名参数repo: SessionRepository | None None是可选具名参数。也就是说必须具名和必须有默认值是两回事*只负责前者是否可选由默认值决定。错误调用会怎样如果尝试按位置传入verboseCliContext(True)Python 会在运行时抛出TypeError提示这些参数只能以关键字方式传递TypeError: CliContext.__init__() takes 2 positional arguments but 3 were given而使用 Pyright / basedpyright 等静态类型检查器时仓库中有 enable-pyright-type-checking-in-cursor 和 set-up-pyright-type-checking-in-github 相关笔记编辑器会在调用前就标出call-arg错误把这类问题消灭在编码阶段而非运行时。实战场景二*rest收集位置参数 具名分隔符同一个规则同样适用于收集位置参数的场景。此时*rest会接收所有剩余的位置实参而它之后声明的参数如delimiter依然是 keyword-onlydef build_identifier(first, *rest, delimiter/): if rest is None: return first return delimiter.join([first, *rest])逐参数拆解参数传递方式说明first位置或关键字*之前的普通参数*rest收集多余位置实参收集为元组例如(bell, mas)delimiter只能关键字有默认值/具名后即可自定义实际调用行为如下 print(build_identifier(taco)) taco print(build_identifier(taco, bell, mas, delimiter-)) taco-bell-mas print(build_identifier(taco, bell, mas, -)) taco/bell/mas/-关键就在第三个例子如果你不用具名参数传分隔符而是直接传一个-它根本不会被当作delimiter而是被*rest一并收走成为标识符的一部分最终被默认的分隔符/拼进结果里——于是得到了taco/bell/mas/-。这正是*边界存在的意义位置实参永远到不了delimiter要修改分隔符就必须写delimiter-。一个小细节示例里的if rest is None判断其实永远不会为真——*rest收集不到任何实参时得到的是空元组()而不是None。真正实用的写法是if not rest: return first但这不影响本笔记要演示的位置收集 关键字专属语法边界。术语澄清positional argument collector 与 Named Argumentspositional argument collector位置参数收集器这一说法出自《Python in a Nutshell, 4th Edition》参见该书第 96 页指的就是*在函数签名中收集多余位置实参的作用。在仓库的另一篇笔记 strictly-separate-positional-and-keyword-arguments 中作者还特别做了一个术语层面的澄清更恰当的称呼是Named Arguments具名参数而非 Keyword Arguments关键字参数因为调用时它是通过名字来绑定实参的。理解这一点有助于把*之后的参数理解为必须呼其名的参数。进阶组合用/和*严格分离位置与关键字*只管关键字专属如果还想要位置专属就要组合位置专属标记/。在 strictly-separate-positional-and-keyword-arguments 中展示了这样的connect函数def connect(host, port, /, *, timeout30): print(fConnecting to #{host}:#{port}) print(f Timeout: {timeout}s) # .../之前host、port必须按位置传*之后timeout必须按关键字传。两者结合后位置参数和关键字参数之间出现了严格的物理边界 connect(localhost, 3000, timeout20) Connecting to #localhost:#3000 Timeout: 20s connect(hostlocalhost, port4000) Traceback (most recent call last): File /Users/lastword/dev/misc/python-experiments/arguments.py, line 37, in module connect(hostlocalhost, port4000) TypeError: connect() got some positional-only arguments passed as keyword arguments: host, port第二次调用把host、port当关键字传立即抛出运行时TypeError——因为它们被定义为 positional-only arguments仅位置参数。在编辑器中这两行同样会被静态类型检查器标记为call-arg: Unexpected keyword argument port for connect。这套组合让函数签名精确到哪些必须按位、哪些必须具名把误用彻底挡在门外。与 dataclass 的对应关系kw_onlyTrue与KW_ONLY*的强制具名思想同样延伸到了dataclass。仓库笔记 configure-other-attributes-of-dataclass-field 展示了用field(kw_onlyTrue)让字段成为关键字专属from dataclasses import dataclass, field from datetime import datetime dataclass class Session: start_time: datetime project_name: str tags: list[str] field(default_factorylist, kw_onlyTrue) end_time: datetime | None field(defaultNone, kw_onlyTrue) # ... sesh1 Session(start1, my-project, tags[pytorch, numpy]) sesh2 Session(start2, other-project, end_timedatetime.now())另一篇 another-way-to-mark-keyword-only-dataclass-fields 则展示了用KW_ONLY哨兵值标记边界的写法——它被原作者评价为更接近标准函数定义语法即本篇所讲的*只是偏魔法from dataclasses import dataclass, field, KW_ONLY from datetime import datetime dataclass class Session: start_time: datetime project_name: str _: KW_ONLY tags: list[str] field(default_factorylist, kw_onlyTrue) end_time: datetime | None None # ... sesh1 Session(start1, my-project, tags[pytorch, numpy]) sesh2 Session(start2, other-project, end_timedatetime.now())_这个伪字段只用来宣告关键字专属边界它本身不会成为 dataclass 的真实字段。对比标准函数写法def build_session(start_time, project_name, *, tags, end_timeNone)可以看到dataclass的KW_ONLY与函数定义的*在语义上完全同构边界之后的所有东西都必须是具名传递。适用场景与最佳实践小结综合仓库内这几篇互相呼应的笔记使用*或对应的kw_only/KW_ONLY的推荐场景是配置类 / 上下文对象如CliContext(verboseTrue)让布尔开关和可选依赖在调用点自文档化多参数 API 的防错位多个同类型参数尤其多个布尔值放在*之后强制调用方写清名字位置参数的尾部保护如build_identifier的delimiter避免位置实参误吞关键字专属参数与/组合实现全约束签名def connect(host, port, /, *, timeout30)两头都锁死dataclass 设计用kw_onlyTrue或KW_ONLY让可选字段必须具名构造时更清晰。一句话总结*是 Python 里最便宜的防呆设计之一——它不产生任何运行时开销却能强制所有调用者把我要的是什么写在代码里配合类型标注与静态检查器让接口的可读性和健壮性同时上一个台阶。延伸阅读本仓库相关笔记strictly-separate-positional-and-keyword-arguments/位置专属标记与*关键字专属标记的组合使用configure-other-attributes-of-dataclass-field用field(kw_onlyTrue)把 dataclass 字段设为关键字专属another-way-to-mark-keyword-only-dataclass-fields用KW_ONLY哨兵值声明关键字专属边界enable-pyright-type-checking-in-cursor / set-up-pyright-type-checking-in-github用静态类型检查在调用点提前拦截违反具名约束的代码赞分享文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载相关推荐ES6函数参数剩余参数gh_mirrors/es/es6features项目应用ES6函数参数剩余参数gh_mirrors/es/es6features项目应用 在JavaScript开发中你是否还在为处理不确定数量的函数参数而编写冗长告别arguments的混乱ES6剩余参数让函数传参优雅起来告别arguments的混乱ES6剩余参数让函数传参优雅起来 在JavaScript开发中函数传参是我们每天都会遇到的基础操作。ECMAScript 6Esh 库参数传递完全指南位置参数、关键字参数与底层编译原理sh 库参数传递完全指南位置参数、关键字参数与底层编译原理 本文聚焦 Python 进程启动库 sh 的参数传递机制系统讲解位置参数必须一参数一字符串的开发工具上一篇10分钟精通暗黑破坏神2存档修改器Diablo Edit2终极实战指南下一篇Diablo Edit2终极暗黑破坏神2存档修改器完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询