Python 函数参数中的反斜杠 “/ ” 是怎么回事?
你有没有在看别人 Python 代码的时候,突然看到一行:
deffunc(a, b, /, c, d):
...
当场一愣:这斜杠是啥?除法吗?写函数里干嘛用的?
今天就把这个事说清楚,讲完你以后再看到 / 就不会慌了,甚至还会想:嗯,这人 API 设计还挺讲究。
先看一段最简单的代码:
defadd(x, y, /, z):
return x + y + z
print(add(1, 2, 3)) # ✅ 正常
print(add(1, y=2, z=3)) # ❌ 报错
运行第二行会直接炸:
TypeError: add() got some positional-only arguments passed as keyword arguments: 'y'
重点来了:
/左边的参数(这里是x, y)叫“仅限位置参数”(positional-only)/右边的(这里的z)是普通参数,可以位置也可以关键字
翻成人话就是:x 和 y 只能这样传:add(1, 2, 3),不能写成 add(x=1, y=2, z=3)。 但 z 无所谓:add(1, 2, z=3) 也是可以的。
Python 里函数参数,其实可以被分成三种:
只能位置传的(在 /左边)位置或关键字都行的(在 /和*中间)只能关键字传的(在 *右边)
我们写个稍微全一点的例子:
defdemo(a, b, /, c, d=0, *, e, f=1):
print(a, b, c, d, e, f)
这里参数类型是这样的:
a, b:只能位置参数c, d:位置或关键字都行e, f:只能关键字参数(因为在*后面)
我们试几种调用方式:
demo(1, 2, 3, 4, e=5) # ✅ OK
demo(1, 2, c=3, e=5) # ✅ OK
demo(1, 2, 3, e=5, f=6) # ✅ OK
demo(a=1, b=2, c=3, e=5) # ❌ 不行,a/b 不能用关键字
demo(1, 2, 3, 4, 5) # ❌ 不行,e 必须写成关键字参数
你脑子里可以画一条轴:
[a, b] / [c, d] * [e, f]
只位置 两用 只关键字
/ 就像一个“分割线”,告诉解释器——左边的参数别想用关键字来传。
为啥要搞这么个看起来多余的 /?
一开始我看到这个语法,第一反应也是: “啊这,不是给自己找麻烦吗,能用关键字传不是挺好的吗?”
后来发现主要有三大原因。
1. 帮你保护 API,不被乱用关键字
想象你写了一个工具函数:
defclamp(value, min_value, max_value):
...
很多人就会开始这样用:
clamp(value=10, min_value=0, max_value=100)
看起来挺优雅,但有个坑:以后你如果想把 min_value 改名成 lower,所有用你这个函数的人都得改一遍调用代码。
如果一开始你就这么写:
defclamp(value, min_value, max_value, /):
...
那调用只能是:
clamp(10, 0, 100)
别人根本没法用关键字参数绑死你的参数名,你以后怎么改名字都行,调用方完全不受影响。 这就是 / 的一个核心价值:保证参数位置不变就行,名称你可以以后慢慢改。
2. 跟内置函数的行为对齐
你可能见过这种提示:
help(len)
# len(obj, /)
len 的定义里,本来就是用 / 标出来的:obj 只能位置传。 像这些 C 实现的内置函数(len, range, list.append 之类),很多早就只支持位置参数了,后来 Python 把这个规则用 / 的方式“写在脸上”,也让我们自己写函数时能享受同样的设计能力。
比如模仿 pow:
defmy_pow(x, y, /, mod=None):
if mod isNone:
return x ** y
return (x ** y) % mod
调用方式会长这样:
my_pow(2, 10) # ✅
my_pow(2, 10, mod=3) # ✅
my_pow(x=2, y=10) # ❌ 不行
看起来是不是就和内置函数的味道接近了?
3. 避免歧义,让签名更清晰
还有一种情况是防误用。 比如你写了一个函数:
defmove(dx, dy, /, *, relative=True):
...
这就很一目了然:
前两个参数 dx, dy是“位移量”,肯定是按顺序传更自然relative这种配置型参数,强制要求用关键字写出来:relative=False,可读性也好很多
如果不用 / 和 *,所有东西都随便写,很快就变成了“猜这个参数到底是干嘛的”游戏。
实战里到底要不要用 “/”?
如果你平时写的是自己用的小脚本,其实不用也无所谓。 但有几种情况,强烈推荐你考虑加上 /:
这是个 给别人用的库函数 / SDK 接口 你觉得以后有可能会改参数名(但不想炸掉所有调用方) 你希望有些参数就是“按顺序记住”,别用关键字乱写 你想让自己的函数签名看起来更严谨、更像“标准库风格”
简单给你一个“模板”,以后可以直接照着用:
defapi(value, scale, /, base=10, *, debug=False):
"""
value, scale:只能按顺序传
base:可位置可关键字(一般推荐关键字)
debug:只能关键字
"""
if debug:
print(f"value={value}, scale={scale}, base={base}")
return (value * scale) / base
最后你只要记住一句话就够了:
函数参数里,
/左边的是“只能用位置”的, 你在设计对外 API、工具函数、库函数时,它是帮你“锁住调用方式”的一个小机关。
等你哪天在源码里看到一串:def func(a, b, /, c, d, *, e):, 心里就可以很淡定地说一句:哦,这设计还挺讲究的 😏