昨天晚上十一点多我还在公司写脚本,准备优雅地搞一个小工具。写着写着,函数参数那块手一抖敲了个斜杠 /,结果一运行直接给我来一句:
TypeError: func() got some positional-only arguments passed as keyword arguments:
当时人都懵了:这斜杠又不是除号,放参数里是干嘛的?
索性今天就把这个事儿跟你聊清楚,一次搞懂。
/ 到底干啥的先看一段最简单的代码:
defadd(x, y, /, z):
return x + y + z
这个函数有三个参数:x、y、z,中间多了个 /。 这个斜杠的意思就一句话:
斜杠左边的参数,只能用“位置”传,不能用“关键字”传。
也就是说:
add(1, 2, 3) # OK
add(1, 2, z=3) # OK,z 在斜杠右边
add(x=1, y=2, z=3) # ❌ 报错,x 和 y 是“只能位置传”的
所以你可以把 / 理解成一个分界线:
如果再加上星号 *,完整一点就像这样:
defdemo(a, b, /, c, d, *, e, f):
...
这个就分三块了:
/ 左边 a, b:只能位置传c, d:位置、关键字都行* 右边 e, f:只能关键字传(这个你可能早就见过)你可能会说:不写 / 也能好好传参数啊,搞这么复杂干嘛。 这个主要是给库作者用的,你自己写业务逻辑平时用得不多,但懂一下很有用。
想象一个场景:你写了个工具函数:
defmove(x, y):
...
大家用得很开心:
move(10, 20)
move(x=10, y=20)
过了一段时间,你突然觉得 x, y 这个名字不好,想改成 longitude, latitude,但线上到处有人这样写:
move(x=10, y=20)
你如果直接改名,就全挂了,对吧。
如果一开始你这么写:
defmove(x, y, /):
...
那调用方式就只允许:
move(10, 20) # ✅
move(x=10, y=20) # ❌ 直接一开始就不让你这么写
这样过一段时间你想改名:
defmove(longitude, latitude, /):
...
外面的人根本感觉不到变化,因为大家一直都是用位置来传的。 所以 / 有点像是在说:“参数名字只是我自己用来看的,不承诺给你当 API 的一部分。”
顺带一提,很多内置函数本来就是这样的,比如 len(obj, /),你从来没写过 len(obj=xxx) 吧。
/说人话就是:当你不想让别人依赖你的“参数名”时,就可以考虑加个 /。
举几个更贴地气的例子。
比如写一个画点的函数:
defdraw_point(x, y, /, color="black", size=3):
print(x, y, color, size)
你期望的用法是这样的:
draw_point(10, 20) # 默认黑色、小点
draw_point(10, 20, color="red") # 改颜色
draw_point(10, 20, size=10) # 改大小
draw_point(10, 20, "blue", 5) # 位置传也行
但你不希望有人写:
draw_point(x=10, y=20, color="red")
因为未来你可能会把 x, y 换成别的含义,比如屏幕坐标 / 地图坐标,名字要改就能随便改,不会把大家的代码搞挂。
有时候你写的是“别人要实现”的接口,比如:
defhandle_event(x, y, /, event_type):
...
你只关心「调用顺序」对就行,根本不想让人用 x=xxx 这种形式。 加个 /,别人就老老实实写:
handle_event(100, 200, "click")
不至于有人写出:
handle_event(y=200, x=100, event_type="click") # 看着就离谱
/ 和 * 一起用长啥样来个稍微完整一点的例子,顺便把参数三种类型串起来:
defresize(width, height, /, scale=1.0, *, keep_ratio=True):
print(width, height, scale, keep_ratio)
调用方式会变成:
resize(1920, 1080) # OK
resize(1920, 1080, 0.5) # OK
resize(1920, 1080, scale=0.5) # OK
resize(width=1920, height=1080) # ❌ 不行,前两个是位置-only
resize(1920, 1080, keep_ratio=False) # ❌ 不行,keep_ratio 必须关键字传
resize(1920, 1080, 0.5, keep_ratio=False) # ✅ 完整体
简单记一下就行:
/ 左边:只能位置/ 和 * 中间:位置 or 关键字* 右边:只能关键字你以后要是不小心写成这样:
defadd(x, y, /):
return x + y
add(x=1, y=2)
报错一般是这种:
TypeError: add() got some positional-only arguments passed as keyword arguments: 'x, y'
大概意思就是: “哥们,这俩参数是只能位置传的,你别写成关键字了。”
看到这种提示,不要怀疑人生,回去看看函数定义里是不是有 / 就行。
大概就这些,其实这个斜杠一开始看着有点怪,用习惯就还挺顺手的。 你下次写公共库或者工具函数的时候,可以试着加一两个 / 体验下,别一上来全加,团队同事会打你 😄