8
def fun(a, b, c, d):
    print('a:', a, 'b:', b, 'c:', c, 'd:', d)

why this one works

fun(3, 7, d=10, *(23,))

and prints out:

a: 3 b: 7 c: 23 d: 10

while this

fun(3, 7, c=10, *(23,))

does not

Traceback (most recent call last):
  File "/home/lookash/PycharmProjects/PythonLearning/learning.py", line 10, in <module>
    fun(3, 7, c=10, *(23,))
TypeError: fun() got multiple values for argument 'c'
Łukasz
  • 1,980
  • 6
  • 32
  • 52
  • I don't know the exact reason that error is thrown, but you should never have variable arguments after keyword arguments. Normally an error is thrown but tuple unpacking seems to throw a different error than the normal `SyntaxError: positional argument follows keyword argument`. – Error - Syntactical Remorse Nov 20 '19 at 19:16
  • @Error-SyntacticalRemorse. I think you're missing the point. OP has found a case where you can actually put a positional after a keyword, and it works. – Mad Physicist Nov 20 '19 at 19:17
  • @MadPhysicist I see that. But I was more just making a statement that tuple unpacking shouldn't take place after keyword arguments. Thus I made it as a comment not an answer. – Error - Syntactical Remorse Nov 20 '19 at 19:18
  • 1
    Awesome question. Never knew this was a thing. – Error - Syntactical Remorse Nov 20 '19 at 19:21

1 Answers1

6

With *(23,), you are unpacking the values in the tuple (23,) as positional arguments, following the positional arguments that are already defined, namely 3 for a and 7 for b, so 23 would be assigned to parameter c, which is why fun(3, 7, d=10, *(23,)) works, but in fun(3, 7, c=10, *(23,)) you are also assigning to value 10 to c as a keyword argument, so it is considered a conflict as c cannot be assigned with both 23 and 10.

Note that while legal, it is discouraged by some to unpack iterable arguments after keyword arguments, as discussed here, although the syntax is ultimately ruled to stay.

blhsing
  • 91,368
  • 6
  • 71
  • 106
  • It's clear what's happening, but since when can you unpack positionals after keywords? – Mad Physicist Nov 20 '19 at 19:20
  • Since Python 3. – blhsing Nov 20 '19 at 19:20
  • Do you have a source for that? It it intentional and documented, or arguably a bug/omission? – Mad Physicist Nov 20 '19 at 19:21
  • Tuple unpacking must take place before keyword arguments in terms of order of operation. Could be a an omission... – Error - Syntactical Remorse Nov 20 '19 at 19:23
  • 1
    @MadPhysicist My bad. I was thinking of something else. This syntax is actually legal in Python 2 as well. The primary use case, as evident from the CPython test case of `f(1,2,c=3,*d,**e)` in [test_ast.py](http://github.com/python/cpython/blob/master/Lib/test/test_ast.py#L207), is to allow a wrapper function to pass variable arguments and keyword arguments to the wrapped function in a more intuitive way, by placing both the variable arguments and variable keyword arguments in the end of the argument list. – blhsing Nov 20 '19 at 19:53
  • @blhsing. Yup, read the bug report, which GvR said is not a bug. Nice to know one more piece of Python arcana. Now that it's pretty clear this isn't going away any time soon, I can't wait to start using it inappropriately :) – Mad Physicist Nov 20 '19 at 19:55
  • Could you put the link back into your answer? I think it makes for a very useful source. – Mad Physicist Nov 20 '19 at 19:57
  • @MadPhysicist Rolled back the edit as suggested, though I do think GvR's decision is right since the use case of a wrapper function as I described above is quite legitimate indeed. – blhsing Nov 20 '19 at 20:01
  • @blhsing. Agreed. I don't think there's anything there that makes this syntax inviolate. – Mad Physicist Nov 20 '19 at 20:10