Python 3.8 支持只接受位置参数了

标签:Python

今天在看 functools.cache 的源码时发现参数列表有个诡异的 /
def cache(user_function, /):
    'Simple lightweight unbounded cache.  Sometimes called "memoize".'
    return lru_cache(maxsize=None)(user_function)
翻了下文档才发现这个叫 Positional-Only Parameters

作用就是这个参数之前的参数都是位置参数,不能用参数名来传递,例如:
>>> def a(x, /, y):
...   return x + y
...
>>> a(1, 2)
3
>>> a(1, y=2)
3
>>> a(x=1, y=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: a() got some positional-only arguments passed as keyword arguments: 'x'
>>> a(1, **{'y': 2})
3

而如果只允许用参数名来传递的话(Keyword-Only Arguments),可以用 * 来分隔:
>>> def b(x, /, y, *, z):
...   return x + y + z
...
>>> b(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: b() takes 2 positional arguments but 3 were given
>>> b(1, 2, z=3)
6
>>> b(1, y=2, z=3)
6
>>> b(1, 2, **{'z': 3})
6
>>> b(1, **{'z': 3}, y=2)
6

这个语法的主要作用是这些位置参数会占用参数名,导致后续的参数没法使用这些参数名了,但这些参数名有时候又是不可控的。
常见的现象就是写装饰器时经常会带上 *args**kwargs 以便接收任意参数,但如果还有一些自定义的参数,就可能和 kwargs 冲突了:
>>> def c(x, **kwargs):
...   print(x, kwargs)
...
>>> c(1)
1 {}
>>> c(1, **{'y': 1})
1 {'y': 1}
>>> c(1, **{'x': 1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: c() got multiple values for argument 'x'
关键是这种现象还没法避免,你不知道其他地方会怎么调用这个函数,为了避免冲突经常会把参数名加个下划线,但现在可以把位置参数分隔开了,也就不会冲突了:
>>> def d(x, /, **kwargs):
...    print(x, kwargs)
...
>>> d(1, x=2)
1 {'x': 2}
>>> d(1, **{'x': 2})
1 {'x': 2}

至于 Keyword-Only Arguments,我认为用途主要是为了可读性,当一个函数的参数很多,或是有 bool 类型的参数时,用位置参数对于阅读代码的人来说可能完全看不懂,例如:
>>> def fetch(url, timeout, retry):
...   ...
...
>>> fetch('http://127.0.0.1', 1, False)
>>> def fetch(*, url, timeout, retry):
...   ...
...
>>> fetch('http://127.0.0.1', 1, False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: fetch() takes 0 positional arguments but 3 were given
>>> fetch(url='http://127.0.0.1', timeout=1, retry=False)

0条评论 你不来一发么↓

    想说点什么呢?