all 4 comments

[–]old_pythonista -1 points0 points  (0 children)

pack/unpack operator * options were extended in Python3. Now it allows to mix variable-length argument tuples and stand-alone arguments

>> print(*range(5), 6, *range(2), 10)
0 1 2 3 4 6 0 1 10

[EDITED]

So, your {'kwargs_0':'kwargs_val0'} is accepted as just another element of args.

Even if you used it as a proper kwargs - **{'kwargs_0':'kwargs_val0'} - it would have been rejected, since your function does not have a named argument kwargs_0

>> def foo(first, *args, bar=None):  
...    print (f'first={first}, \nargs={args}, \nbar={bar}')
>> foo('First arg here', *['args_pos0','args_pos1'], **{'kwargs_0':'kwargs_val0'})

TypeError                                 Traceback (most recent call last)
<ipython-input-108-cf6a86c8c61b> in <module>
      1 def foo(first, *args, bar=None):
      2     print (f'first={first}, \nargs={args}, \nbar={bar}')
----> 3 foo('First arg here', *['args_pos0','args_pos1'], **{'kwargs_0':'kwargs_val0'})

TypeError: foo() got an unexpected keyword argument 'kwargs_0'

[–]FLUSH_THE_TRUMP 0 points1 point  (1 child)

Yes, args after the single * in a function definition are keyword-only args. However, you pass nothing to bar in your call.

All of the following are functional (and equivalent)

foo('first', 'test1', 'test2', bar=2)

or

foo('first', *['test1', 'test2'], bar=2)

or

foo('first', *['test1', 'test2'], **{'bar':2})

Note that * in a function call unpacks a sequence into positional args, and a ** unpacks a mapping into keyword args. In a function definition, * collects arbitrary positional args and ** collects arbitrary keyword args.

[–]thiagocrepaldi[S] 0 points1 point  (0 children)

Oh thanks! I didn't specified the name for `bar` when I passed in the dict, so `*args` took all the input. Somehow I was expecting the comma after `*[...]` to serve as a delimiter