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);但“只允许关键字”意味着前面没有可传输的参数,所以一开始就没有普通参数
低于 3.8 不能使用的环境 * 语法,只能退而求其次:接受任何关键字参数,然后检查必要的字段,拒绝多余的字段,拒绝位置参数。
很容易踩的坑只是验证 **kwargs 它是否包含必要键,但忘记检查位置参数是否传输——func("x", y=1) 中的 "x" 会被忽视或导致逻辑错误。
立即学习“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),否则,类型检查将是相同的虚假
真正困难的不是写出来,而是让团队中的每个人都明白,这个函数的呼叫是合同的一部分,而不是一个选择。一旦松动,例如,一个临时的位置呼叫绕过校准,整个约束就会失败。