Let’s go in pieces. First, let’s see what the /
ago.
If you declare a function like this:
def f(a, b):
print(a, b)
It is possible to call it several different ways:
# passando os parâmetros posicionais
f(1, 2) # 1 2
# usando os nomes dos parâmetros
f(a=1, b=2) # 1 2
# usando os nomes, podemos inclusive passá-los em qualquer ordem
f(b=2, a=1) # 1 2
But if we use the /
in the declaration of parameters (resource that was added in Python 3.8), that means all arguments before the /
cannot be passed using the names. See the difference for the previous example:
def f(a, b, /): # "a" e "b" não podem ser passados com nomes
print(a, b)
# passando os parâmetros posicionais, funciona
f(1, 2) # 1 2
# usando os nomes, dá erro
f(a=1, b=2) # TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'
And any parameter that is after the /
can be passed with or without the name:
def f(a, b, /, c, d):
print(a, b, c, d)
# "c" e "d" podem ser passados posicionalmente
f(1, 2, 3, 4) # 1 2 3 4
# "c" e "d" podem ser passados com os nomes
f(1, 2, c=3, d=4) # 1 2 3 4
f(1, 2, d=4, c=3) # 1 2 3 4
# "a" e "b" não podem ser passados com nomes
f(a=1, b=2, c=3, d=4) # TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'
Now, if we use keyword Arguments:
def foo(name, **kwds):
print(name, kwds)
By calling foo(1, **{'name' : 2})
, in the end is the same as doing foo(1, name=2)
(see this example in the documentation). But as the first parameter is called name
, then it’s like I’m doing foo(name=1, name=2)
. Hence the error message: "foo() got Multiple values for argument 'name'", because he understands that I am passing two different values to name
.
Already using the /
:
def foo(name, /, **kwds):
print(name, kwds)
foo(1, **{'name' : 2}) # 1 {'name': 2}
foo(1, name=2) # 1 {'name': 2}
Now it works because, like the /
indicates that the parameter name
is just positional, his name (name
) can be used in keyword Arguments, without the possibility of "collision" of names.
Even this example is cited in documentation:
Since the Parameters to the left of /
are not Exposed as possible Keywords, the Parameters Names remain available for use in **kwargs
:
>>> def f(a, b, /, **kwargs):
... print(a, b, kwargs)
...
>>> f(10, 20, a=1, b=2, c=3) # a and b are used in two ways
10 20 {'a': 1, 'b': 2, 'c': 3}
In free translation: "Like the parameters on the left of /
are not exposed as Keywords, their names are available for use in **kwargs
".
The first a
receives the amount 10
, but as it is only a positional parameter (thanks to the /
), there is no ambiguity in the call: I know that the a=1
refers to the kwargs
, since the first a
cannot be called by name.
So what happens is that **{'name' : 2} is the same thing as name = 2, when used as a parameter? I guess I understood then worth
– Henrique Hott
@Henriquehott Yes, inclusive in the documentation has an example that shows this
– hkotsubo