← 返回题目列表

Python 函数定义里的 / 和 * 是什么意思?仅位置、仅关键字参数怎么用?

高频 中等 第 7 / 21 题 更新于 2026/07/31
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 内置函数(lenpowabs)的参数天生就是仅位置的(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) 一目了然)、也防止调用方传错顺序;此外加新参数时设为「仅关键字 + 默认值」能保证向后兼容(老调用不受影响);sortedprint 等内置函数都用了仅关键字参数。
  • 误区:仅位置参数(/)是给普通业务代码用的常规特性。 主要是库作者和内置函数用——/(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 不撞名);五种参数顺序固定;日常多用仅关键字」。