Python 函数定义里的 / 和 * 是什么意思?仅位置、仅关键字参数怎么用?
简化版
在函数定义的参数列表里,/ 和 * 是两个「分隔标记」:/ 之前的参数是「仅位置参数(positional-only)」——只能按位置传、不能用关键字传;* 之后的参数是「仅关键字参数(keyword-only)」——只能用关键字传、不能按位置传。语法:def f(pos_only, /, normal, *, kw_only)——① / 左边(pos_only)只能 f(1, ...)、不能 f(pos_only=1, ...);② / 和 * 之间(normal)位置或关键字都行;③ * 右边(kw_only)只能 f(..., kw_only=3)、不能按位置传。为什么要这样限制:① 仅关键字参数(*)——强制调用方写出参数名,让调用更清晰、防止「一堆布尔/数字按位置传搞混」(如 sorted(data, key=..., reverse=True) 里 reverse 必须写名字);② 仅位置参数(/,3.8+)——参数名不作为「公共接口」的一部分,方便以后改参数名不破坏调用方(很多内置函数如 len(obj) 的 obj 就是仅位置的)。*args 也隐含「后面的都是仅关键字」。核心记忆:/ 前是仅位置(只能按位置传)、* 后是仅关键字(只能按名字传);* 强制写参数名(清晰防混)、/ 让参数名不算接口(可自由改名)。
详细版
参数种类与分隔符:
| 区域 | 参数种类 | 传参方式 |
|---|---|---|
/ 之前 | 仅位置(positional-only,3.8+) | 只能按位置 |
/ 和 * 之间 | 普通(positional-or-keyword) | 位置或关键字 |
* 之后(或 *args 之后) | 仅关键字(keyword-only) | 只能按名字 |
# 完整形态:/ 前仅位置,* 后仅关键字
def f(a, b, /, c, d, *, e, f):
# ^^^^ ^^^^ ^^^^
# 仅位置 普通 仅关键字
...
f(1, 2, 3, 4, e=5, f=6) # ✓
f(1, 2, c=3, d=4, e=5, f=6) # ✓(c,d 可关键字)
# f(a=1, ...) # ✗ a 是仅位置,不能关键字
# f(1,2,3,4,5,6) # ✗ e,f 是仅关键字,不能按位置
# 仅关键字参数(* 后)——强制写参数名
def create_user(name, *, is_admin=False, active=True):
...
create_user("Tom", is_admin=True) # ✓ 必须写 is_admin=
# create_user("Tom", True) # ✗ TypeError(is_admin 只能关键字)
# → 防止 create_user("Tom", True, False) 这种"一堆布尔搞不清"
# *args 后面的参数自动是仅关键字
def g(a, *args, key=None):
... # key 只能关键字传
g(1, 2, 3, key="x") # ✓(2,3 进 args,key 必须写名字)
# 仅位置参数(/ 前,3.8+)——参数名不算接口
def distance(x, y, /):
return (x**2 + y**2) ** 0.5
distance(3, 4) # ✓
# distance(x=3, y=4) # ✗(x,y 仅位置)
# → 好处:以后把 x 改名为 a 不影响调用方(因为调用方不能用 x=)
# 内置函数很多用仅位置(C 实现)
# len(obj)——obj 是仅位置,len(obj=x) 报错
# 例:pow(base, exp, mod, /)
⚠️ 理解要点:
/和*把参数列表切成三段——/左边「只能按位置传」、*右边「只能按名字传」、中间「随意」;这两个标记本身不是参数、只是「边界」。它们各自解决一类问题:*(仅关键字)的目的是「强制清晰」——当函数有多个布尔开关或含义靠位置区分的参数时,逼调用方写出参数名,避免func(data, True, False, None)这种「谁是谁完全靠数位置」的噩梦(sorted(iterable, *, key=None, reverse=False)就是典型,reverse 必须写名字);顺带一提,只要写了*args,它后面的参数也自动变成仅关键字。/(仅位置,Python 3.8 引入)的目的是「解耦参数名」——让参数名不成为「公共 API 契约」的一部分,这样库作者以后想改参数名(def f(x)→def f(value))不会破坏那些用关键字调用的代码;很多 CPython 内置函数(len、pow、abs)的参数天生就是仅位置的(len(obj=x)会报错)。日常写业务代码,*(仅关键字)用得多、很值得用(提升可读性和安全性);/(仅位置)主要是库作者和内置函数用,普通代码用得少。
完整版教学
一、五种参数种类
先建立完整的参数种类地图:
Python 函数参数的五种"种类(kind)":
def f(pos_only, /, normal, *args, kw_only, **kwargs):
^^^^^^^^ ^^^^^^ ^^^^^ ^^^^^^^ ^^^^^^^^
仅位置 普通 可变位置 仅关键字 可变关键字
① POSITIONAL_ONLY(仅位置):/ 之前
只能按位置传,不能用关键字
② POSITIONAL_OR_KEYWORD(普通):/ 和 * 之间
位置或关键字都行(最常见的默认种类)
③ VAR_POSITIONAL(可变位置):*args
收集多余的位置参数成元组
④ KEYWORD_ONLY(仅关键字):* 或 *args 之后
只能用关键字传
⑤ VAR_KEYWORD(可变关键字):**kwargs
收集多余的关键字参数成字典
顺序(固定):
仅位置 → 普通 → *args → 仅关键字 → **kwargs
(用 / 分隔仅位置、用 * 分隔仅关键字)
分隔符本身不是参数:
/ 和 * 只是"边界标记",不占参数位
两种"* 分隔":
def f(a, *, b): # 裸 * → b 是仅关键字(不收集可变位置)
def f(a, *args, b): # *args → 收集可变位置,b 仍是仅关键字
所以五种参数:仅位置(/前)/普通/*args/仅关键字(*后)/**kwargs,顺序固定
Python 函数参数的五种「种类(kind)」:def f(pos_only, /, normal, *args, kw_only, **kwargs)——① POSITIONAL_ONLY(仅位置):/ 之前(只能按位置传)、② POSITIONAL_OR_KEYWORD(普通):/ 和 * 之间(位置或关键字都行、最常见默认)、③ VAR_POSITIONAL(可变位置):*args(收集多余位置参数成元组)、④ KEYWORD_ONLY(仅关键字):* 或 *args 之后(只能关键字传)、⑤ VAR_KEYWORD(可变关键字):**kwargs(收集多余关键字成字典)。顺序(固定):仅位置 → 普通 → *args → 仅关键字 → **kwargs。分隔符本身不是参数:/ 和 * 只是边界标记、不占参数位。两种「 分隔」**:裸 *(b 是仅关键字、不收集可变位置)、*args(收集可变位置、b 仍仅关键字)。理解「五种参数:仅位置(/前)/普通(/与之间)/args 可变位置/仅关键字(后)/**kwargs;顺序固定;/ 和是边界标记不占位;裸不收集、*args 收集」,就建立了参数种类地图。
二、仅关键字参数(* 后)
理解 * 分隔的仅关键字参数:
仅关键字参数:* 之后的参数,只能用关键字传
def f(a, b, *, c, d):
# c, d 只能 f(1, 2, c=3, d=4)
# 不能 f(1, 2, 3, 4)(c,d 不接受位置)
裸 * 的作用:
* 单独出现(不带名字)= "从这里开始都是仅关键字"
def f(a, *, flag): # flag 必须 f(1, flag=True)
*args 后面也是仅关键字:
def f(a, *args, key=None):
# args 收集位置参数、key 只能关键字
f(1, 2, 3, key="x") # 2,3→args, key 必须写名字
为什么用仅关键字——强制清晰:
① 布尔/数字参数靠位置分不清:
✗ create("Tom", True, False, None) # 谁是谁?
✓ create("Tom", is_admin=True, active=False, role=None)
② 强制调用方写出参数名 → 可读、防传错顺序
③ 加新参数不破坏(新的仅关键字参数放最后,带默认值)
内置例子:
sorted(iterable, *, key=None, reverse=False)
→ key、reverse 必须写名字(防 sorted(data, myfunc, True) 混淆)
dict(**kwargs)、print(*args, sep, end, file, flush)
设计建议:
含义靠名字区分的参数(尤其布尔开关、配置)→ 设为仅关键字
→ def f(data, *, verbose=False, timeout=30)
所以仅关键字(*后)只能按名字传,强制清晰(防布尔/顺序混淆),配置参数常设为它
仅关键字参数:* 之后的参数、只能用关键字传(def f(a, b, *, c, d)——c、d 只能 f(1, 2, c=3, d=4)、不能 f(1, 2, 3, 4))。裸 * 的作用:* 单独出现(不带名字)= 从这里开始都是仅关键字(def f(a, *, flag)——flag 必须 f(1, flag=True))。*args 后面也是仅关键字:def f(a, *args, key=None)(args 收集位置、key 只能关键字)。为什么用仅关键字——强制清晰:① 布尔/数字参数靠位置分不清(create("Tom", True, False, None) 谁是谁?→ create("Tom", is_admin=True, ...))、② 强制写出参数名(可读、防传错顺序)、③ 加新参数不破坏(新的仅关键字放最后带默认值)。内置例子:sorted(iterable, *, key=None, reverse=False)(key、reverse 必须写名字)、print(*args, sep, end, ...)。设计建议:含义靠名字区分的参数(尤其布尔开关、配置)设为仅关键字。理解「仅关键字(后)只能按名字传;裸或*args 后都是;强制清晰(防布尔/顺序混淆、加新参数不破坏);sorted 的 key/reverse 是典型;配置/布尔参数设为它」,就掌握了仅关键字参数。
三、仅位置参数(/ 前)
理解 / 分隔的仅位置参数:
仅位置参数:/ 之前的参数,只能按位置传(Python 3.8+)
def f(a, b, /, c):
# a, b 只能 f(1, 2, ...)
# 不能 f(a=1, b=2, ...)
为什么用仅位置——解耦参数名(让参数名不算 API):
① 参数名不成为"公共契约":
def f(x, /): ... # 调用方只能 f(值)、不能 f(x=值)
→ 以后把 x 改名为 value 不破坏调用方
def f(value, /): ... # 调用方 f(值) 照样工作
② **kwargs 里能用同名键不冲突:
def f(a, /, **kwargs): ...
f(1, a=2) # a=2 进 kwargs(不和仅位置的 a 冲突)
③ 性能/明确性(C 实现的内置函数常用)
内置函数的仅位置:
len(obj)——obj 仅位置,len(obj=x) 报错
pow(base, exp, mod=None, /)
abs(x, /)
→ 这些函数的参数名不是接口的一部分
什么时候用仅位置:
① 库/API 作者想保留"改参数名的自由"
② 参数名没有意义(如数学函数的第一个操作数)
③ 想让 **kwargs 能用任意键(不和位置参数撞名)
普通业务代码:
仅位置用得少(业务代码通常希望参数名清晰、可关键字传)
→ / 主要是库作者和内置函数的工具
所以仅位置(/前)只能按位置传,好处解耦参数名(可自由改名)+**kwargs 不撞名,库/内置常用
仅位置参数:/ 之前的参数、只能按位置传(Python 3.8+)(def f(a, b, /, c)——a、b 只能 f(1, 2, ...)、不能 f(a=1, ...))。为什么用仅位置——解耦参数名(让参数名不算 API):① 参数名不成为公共契约(def f(x, /) 调用方只能 f(值)、以后把 x 改名不破坏调用方)、② **kwargs 里能用同名键不冲突(def f(a, /, **kwargs)、f(1, a=2) 时 a=2 进 kwargs 不和仅位置 a 冲突)、③ 性能/明确性(C 实现的内置常用)。内置函数的仅位置:len(obj)(len(obj=x) 报错)、pow(base, exp, mod, /)、abs(x, /)。什么时候用仅位置:① 库/API 作者想保留改参数名的自由、② 参数名没有意义、③ 想让 **kwargs 能用任意键。普通业务代码:仅位置用得少(业务代码通常希望参数名清晰)、/ 主要是库作者和内置的工具。理解「仅位置(/前)只能按位置传(3.8+);好处解耦参数名(不成公共契约、可自由改名)+**kwargs 不撞名+性能;len/pow/abs 等内置用;库作者用得多、业务代码少用」,就掌握了仅位置参数。
四、组合与完整规则
理解 / 和 * 组合使用:
完整参数列表的组合:
def f(pos1, pos2, /, normal, *, kw1, kw2):
→ pos1,pos2 仅位置;normal 普通;kw1,kw2 仅关键字
各段的传参规则:
f(1, 2, 3, kw1=4, kw2=5) # ✓(normal=3 按位置)
f(1, 2, normal=3, kw1=4, kw2=5) # ✓(normal 按关键字)
f(pos1=1, ...) # ✗(pos1 仅位置)
f(1, 2, 3, 4, 5) # ✗(kw1,kw2 仅关键字)
带 *args 和 **kwargs 的完整形态:
def f(po, /, normal, *args, kwo, **kwargs):
→ po 仅位置、normal 普通、args 收集位置、
kwo 仅关键字、kwargs 收集关键字
顺序规则(必须遵守):
仅位置 / 普通 * 仅关键字 **kwargs
或 仅位置 / 普通 *args 仅关键字 **kwargs
→ / 在前、* 或 *args 在中、**kwargs 最后
默认值规则:
有默认值的参数后面不能有无默认值的(同种类内)
def f(a, b=1, c): ... # ✗ 语法错误(c 无默认在 b 有默认后)
→ 但仅关键字参数不受此限(* 后可自由带/不带默认)
def f(a, *, b, c=1): ... # ✓(仅关键字段可以 b 无默认在前)
内省查看参数种类:
inspect.signature(f).parameters[name].kind
→ POSITIONAL_ONLY / POSITIONAL_OR_KEYWORD / KEYWORD_ONLY 等
所以组合:pos/仅位置 / normal 普通 * kw/仅关键字 **kwargs,顺序固定
完整参数列表的组合:def f(pos1, pos2, /, normal, *, kw1, kw2)——pos1、pos2 仅位置、normal 普通、kw1、kw2 仅关键字。各段的传参规则:f(1, 2, 3, kw1=4, kw2=5)(normal 按位置)、f(1, 2, normal=3, kw1=4, kw2=5)(normal 按关键字)、f(pos1=1, ...)(✗ pos1 仅位置)、f(1, 2, 3, 4, 5)(✗ kw1、kw2 仅关键字)。带 *args 和 **kwargs 的完整形态:def f(po, /, normal, *args, kwo, **kwargs)。顺序规则(必须遵守):仅位置 / 普通 */*args 仅关键字 **kwargs(/ 在前、* 或 *args 在中、**kwargs 最后)。默认值规则:有默认值的参数后面不能有无默认值的(同种类内),但仅关键字参数不受此限(def f(a, *, b, c=1) 可以 b 无默认在前)。内省查看:inspect.signature(f).parameters[name].kind。理解「组合 pos 仅位置 / normal 普通 * kw 仅关键字 **kwargs 顺序固定;normal 可位置可关键字;默认值后不能有无默认(同种类)、但仅关键字段不受此限;inspect 查 kind」,就掌握了组合规则。
五、实际应用与设计
理解实际怎么用这些参数种类:
实际应用与设计建议:
① 用仅关键字提升可读性(最实用):
# 布尔开关、配置项设为仅关键字
def connect(host, port, *, timeout=30, retries=3, verbose=False):
connect("localhost", 8080, timeout=60, verbose=True)
→ 调用方必须写名字,一眼看懂每个参数
② 加新参数时用仅关键字(向后兼容):
# 老版本 def process(data)
# 新版本 def process(data, *, batch_size=100)
→ 新参数是仅关键字、有默认 → 不破坏老调用 process(data)
③ 库作者用仅位置解耦参数名:
def compute(x, y, /): # x,y 是实现细节
→ 以后改名不影响调用方
④ 混合(内置函数的做法):
def f(pos, /, both, *, kw):
→ pos 是实现细节、both 灵活、kw 强制清晰
设计原则:
✓ 布尔/配置参数 → 仅关键字(*)
✓ 加新参数 → 仅关键字 + 默认值(兼容)
✓ 参数名是实现细节 → 仅位置(/)
✗ 别过度用 /(业务代码通常希望参数名清晰)
常见收益:
仅关键字:调用清晰、防传错顺序、易扩展
仅位置:改名自由、**kwargs 不撞名
所以设计:布尔/配置用仅关键字(*清晰)、加参数用仅关键字兼容、实现细节用仅位置(/)
实际应用与设计建议:① 用仅关键字提升可读性(最实用)(布尔开关、配置项设为仅关键字,def connect(host, port, *, timeout=30, verbose=False)、调用方必须写名字一眼看懂)、② 加新参数时用仅关键字(向后兼容)(新参数是仅关键字+默认值不破坏老调用)、③ 库作者用仅位置解耦参数名(def compute(x, y, /) 以后改名不影响调用方)、④ 混合(内置函数的做法)(def f(pos, /, both, *, kw))。设计原则:✓ 布尔/配置参数用仅关键字(*)、✓ 加新参数用仅关键字+默认值(兼容)、✓ 参数名是实现细节用仅位置(/)、✗ 别过度用 /(业务代码通常希望参数名清晰)。理解「设计:布尔/配置用仅关键字(*清晰易读)、加新参数用仅关键字+默认值(兼容)、实现细节用仅位置(/改名自由)、别过度用/;仅关键字收益调用清晰防传错、仅位置收益改名自由」,就掌握了实际应用。
六、总结与实践
总结参数标记:
核心:/ 和 * 把参数列表切三段
/ 之前:仅位置(只能按位置传)
/ 和 * 之间:普通(位置或关键字)
* 之后(或 *args 后):仅关键字(只能按名字传)
五种参数种类:
仅位置 / 普通 / *args / 仅关键字 / **kwargs
* 分隔(仅关键字):
裸 * 或 *args 后都是仅关键字
目的:强制写参数名(清晰、防混、易扩展)
例:sorted(it, *, key, reverse)
/ 分隔(仅位置,3.8+):
目的:参数名不算 API(可自由改名)+ **kwargs 不撞名
例:len(obj)、pow(base, exp, /)
库作者/内置用,业务代码少用
设计建议:
① 布尔/配置参数 → 仅关键字(提升可读、防传错顺序)
② 加新参数 → 仅关键字 + 默认值(向后兼容)
③ 参数名是实现细节 → 仅位置
顺序规则:
仅位置 / 普通 *args 仅关键字 **kwargs(固定)
核心总结:
/ 前仅位置(只能位置传)、* 后仅关键字(只能名字传)
* 强制清晰、/ 解耦参数名
日常多用仅关键字(*),/ 主要库作者用
所以 / 前仅位置、* 后仅关键字;* 强制写名字(清晰)、/ 让参数名可改;日常多用*
核心:/ 和 * 把参数列表切三段——/ 之前仅位置(只能按位置传)、/ 和 * 之间普通、* 之后(或 *args 后)仅关键字(只能按名字传)。五种参数种类:仅位置 / 普通 / *args / 仅关键字 / **kwargs。* 分隔(仅关键字):裸 * 或 *args 后都是仅关键字、目的强制写参数名(清晰、防混、易扩展、sorted(it, *, key, reverse))。/ 分隔(仅位置,3.8+):目的参数名不算 API(可自由改名)+ **kwargs 不撞名(len(obj)、库作者/内置用、业务少用)。设计建议:布尔/配置参数用仅关键字、加新参数用仅关键字+默认值、参数名是实现细节用仅位置。理解「/ 前仅位置(只能位置传)、* 后仅关键字(只能名字传);* 强制清晰(sorted 的 key/reverse)、/ 解耦参数名(len/pow);布尔配置用*、加参数用*兼容、实现细节用/;日常多用仅关键字」,就掌握了总结与实践。
记忆钩子:「函数定义参数列表里的 / 和 * 是两个『分隔标记』(本身不是参数、只是边界):★/ 之前的参数是『仅位置参数(positional-only、Python 3.8+)』——只能按位置传、不能用关键字(def f(a,/):只能 f(1)、不能 f(a=1));★* 之后的参数是『仅关键字参数(keyword-only)』——只能用关键字传、不能按位置(def f(a,,b):只能 f(1,b=2)、不能 f(1,2));中间的是普通参数(位置或关键字都行);完整形态 def f(pos,/,normal,,kw)切三段;五种参数种类顺序固定:仅位置/普通/args/仅关键字/**kwargs(args 后面的参数也自动是仅关键字);★为什么用仅关键字():强制调用方写出参数名、让调用清晰、防止『一堆布尔/数字按位置传搞混』(sorted(iterable,,key=None,reverse=False)里 key/reverse 必须写名字,防 sorted(data,func,True)混淆),且加新参数(仅关键字+默认值)不破坏老调用;★为什么用仅位置(/):让参数名不成为『公共 API 契约』,库作者以后改参数名(x→value)不破坏用关键字调用的代码,且能让kwargs 用同名键不冲突,很多内置函数(len(obj)、pow、abs)天生仅位置(len(obj=x)报错);日常业务代码多用仅关键字(*)提升可读、/ 主要库作者和内置用」**。
七、常见误区与追问
- 误区:函数定义里的 / 是除法、 是乘法。* 在参数列表里它们是「分隔标记」不是运算符:
/标记「它左边的参数都是仅位置参数(只能按位置传)」、*(单独出现时)标记「它右边的参数都是仅关键字参数(只能按名字传)」;它们本身不是参数、不占参数位置;比如def f(a, /, b, *, c)里 a 仅位置、b 普通、c 仅关键字。 - 误区: 只能写成 args 来收集可变位置参数。 不是——
*有两种用法:①*args收集多余的位置参数成元组;② 单独的裸*(不带名字)不收集任何东西、只是标记「从这里开始的参数都是仅关键字」;def f(a, *, b)里的*就是裸星号、表示 b 只能用关键字传;另外用了*args时,*args后面的参数也自动变成仅关键字(因为位置参数都被 args 收走了)。 - 误区:仅关键字参数( 后)没什么用,普通参数就够了。* 很有用——把「布尔开关、配置项」设为仅关键字能大幅提升可读性和安全性:强制调用方写出参数名,避免
create_user("Tom", True, False, None)这种「每个位置是什么全靠数」的调用(改成create_user("Tom", is_admin=True, active=False)一目了然)、也防止调用方传错顺序;此外加新参数时设为「仅关键字 + 默认值」能保证向后兼容(老调用不受影响);sorted、print等内置函数都用了仅关键字参数。 - 误区:仅位置参数(/)是给普通业务代码用的常规特性。 主要是库作者和内置函数用——
/(Python 3.8 引入)的目的是「让参数名不成为公共 API 的一部分」,方便库作者以后自由修改参数名而不破坏用关键字调用的代码,以及让**kwargs能用和位置参数同名的键;很多 CPython 内置函数(len、pow、abs)的参数天生是仅位置的;但普通业务代码通常希望参数名清晰、允许调用方用关键字传(更可读),所以业务代码里/用得少、*(仅关键字)用得多。 - *追问:def f(a, b, /, c, d, , e, f) 里各个参数怎么传? 分三段:① a、b 在
/之前是仅位置参数,只能按位置传(f(1, 2, ...)),不能写f(a=1);② c、d 在/和*之间是普通参数,位置或关键字都行(f(1, 2, 3, 4, ...)或f(1, 2, c=3, d=4, ...));③ e、f 在*之后是仅关键字参数,只能按名字传(f(..., e=5, f=6)),不能按位置;所以合法调用如f(1, 2, 3, 4, e=5, f=6)或f(1, 2, c=3, d=4, e=5, f=6);非法的如f(a=1, ...)(a 仅位置)、f(1, 2, 3, 4, 5, 6)(e、f 仅关键字不能按位置)。 - 追问:为什么要设计「仅关键字参数」,它解决什么问题? 主要解决「调用可读性和防错」的问题:当函数有多个含义靠位置区分的参数(尤其是布尔开关、数字配置)时,按位置调用非常容易搞混、也难读——
func(data, True, False, 30, None)里每个值是什么全靠数位置和记忆;把这些参数设为仅关键字(放在*后),就强制调用方写出参数名(func(data, verbose=True, cache=False, timeout=30, key=None)),一眼看懂、也不怕传错顺序;另一个好处是「向后兼容地扩展」:给已有函数加新参数时,把新参数设为「仅关键字 + 带默认值」,老的调用代码(不传这个参数)完全不受影响,而且新参数必须写名字、不会和已有的位置参数产生歧义;sorted(iterable, *, key=None, reverse=False)就是典范。 - 追问:仅位置参数(/)除了「改参数名自由」,还有什么用途? 主要有三个用途:① 解耦参数名与公共接口——库作者把参数设为仅位置后,参数名就不是 API 契约的一部分了,以后想改名(比如把
def f(x)改成def f(value))不会破坏任何用关键字调用的代码(因为调用方本来就不能用关键字传);② 让**kwargs能自由使用任意键——def f(a, /, **kwargs)里,调用f(1, a=2)时 a=2 会进 kwargs(不和仅位置的参数 a 冲突),这对「参数名不确定、要透传任意关键字」的函数很有用;③ 性能和明确性——很多 C 实现的内置函数(len、abs、pow)用仅位置参数,一方面参数名对用户无意义(如数学函数的操作数)、另一方面避免关键字解析的开销;总的来说 / 是「库作者/API 设计」层面的工具,用来控制接口契约和参数透传,普通业务代码用得不多。
八、加强记忆
函数定义参数列表里的 / 和 * 是两个「分隔标记」(本身不是参数、只是边界):/ 之前的参数是「仅位置参数(positional-only、Python 3.8+)」——只能按位置传、不能用关键字(def f(a, /):只能 f(1)、不能 f(a=1));* 之后的参数是「仅关键字参数(keyword-only)」——只能用关键字传、不能按位置(def f(a, *, b):只能 f(1, b=2)、不能 f(1, 2));中间的是普通参数(位置或关键字都行)。完整形态 def f(pos, /, normal, *, kw) 切三段;五种参数种类顺序固定:仅位置 / 普通 / *args / 仅关键字 / **kwargs(*args 后面的参数也自动是仅关键字)。为什么用仅关键字(*):强制调用方写出参数名、让调用清晰、防止「一堆布尔/数字按位置传搞混」(sorted(iterable, *, key=None, reverse=False) 里 key/reverse 必须写名字),且加新参数(仅关键字+默认值)不破坏老调用。为什么用仅位置(/):让参数名不成为「公共 API 契约」,库作者以后改参数名不破坏用关键字调用的代码,且能让 **kwargs 用同名键不冲突,很多内置函数(len(obj)、pow、abs)天生仅位置。日常业务代码多用仅关键字(*)提升可读、/ 主要库作者和内置用。一句话「**/ 前仅位置(只能按位置传)、* 后仅关键字(只能按名字传)、中间普通;* 强制写参数名(清晰防混、易扩展)、/ 解耦参数名(可自由改名、kwargs 不撞名);五种参数顺序固定;日常多用仅关键字」。