Python 3.8+用*可强制仅关键字参数,如def send_email(*, to, subject, body),调用必须显式命名参数,否则报错;其核心是将所有参数置于*后且前方无位置参数,体现调用契约。

Python 3.8+:用 / 和 * 明确分隔参数类型
Python 3.8 引入了仅限位置参数(/)和仅限关键字参数(*)的语法,这是实现“只允许关键字传参”的最直接方式。关键不是只用 *,而是把所有参数放在 * 后面,且前面不放任何可位置传的参数。
常见错误是写成 def f(*, a, b) 却仍尝试 f(1, 2)——这会报 TypeError: f() takes 0 positional arguments but 2 were given,但用户可能误以为是函数没定义好,其实是调用姿势错了。
-
def send_email(*, to: str, subject: str, body: str)—— 正确:必须写成send_email(to="a@b.c", subject="Hi", body="...") - 不能省略参数名,哪怕只有一个参数:
send_email("a@b.c", ...)直接报错 - 如果函数已有部分位置参数,又想强制后续为关键字,用
def f(a, b, *, c, d);但“只允许关键字”意味着前面不能有可位置传的参数,所以开头不能有普通参数
兼容旧版本(Py**kwargs + 手动校验
低于 3.8 的环境无法用 * 语法,只能退而求其次:接受任意关键字参数,再显式检查必需字段、拒绝多余字段、拒绝位置参数。
容易踩的坑是只校验 **kwargs 是否包含必要键,却忘了检查是否传了位置参数——func("x", y=1) 中的 "x" 会被忽略或引发逻辑错误。
立即学习“Python免费学习笔记(深入)”;
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
- 必须在函数开头检查
len(args) == 0,否则位置参数悄悄溜进来了 - 用
required = {"to", "subject", "body"}和if not required.issubset(kwargs.keys())校验必填项 - 额外建议加
if kwargs.keys() - required:警告或拒绝未知参数,避免静默忽略拼写错误(如subjet)
为什么不用 @functools.wraps 或装饰器自动处理?
有人想封装一个通用装饰器,比如 @keyword_only,自动拦截位置参数并校验关键字。理论上可行,但实际中问题不少:
- 装饰器无法改变 Python 解析调用时的参数绑定行为,
f(1, 2)在进入装饰器前就已经被解释为两个位置参数,你只能在运行时抛错,无法让 IDE 或类型检查器(如 mypy)提前感知 - 签名(
inspect.signature)会被装饰器污染,导致文档生成、自动补全、help()显示混乱 - 性能上多一层调用开销,对高频函数(如数据处理内层循环)不友好
除非项目强约束所有函数都走统一网关(如 API 层),否则不如直接用原生 * 语法或手写校验更透明可控。
类型提示与静态检查配合使用
仅靠运行时限制不够。mypy 等工具能提前发现错误调用,但前提是签名正确标注。注意 * 本身不带类型信息,需配合 typing 注解。
- 正确写法:
def load_config(*, path: str, encoding: str = "utf-8") -> dict: ... - mypy 能识别
load_config("x.yml")是错的,并提示 “Too many positional arguments” - 如果用了
**kwargs方案,必须加-> None并配# type: ignore或自定义协议(Protocol),否则类型检查形同虚设
真正难的不是写出来,而是让团队所有人一致理解:这个函数的调用方式是契约的一部分,不是可选项。一旦松动,比如某处临时改成位置调用绕过校验,整个约束就失效了。

















