Mohammad Rahimi
Mohammad Rahimi

Reputation: 1123

Dictionary Unpacking Operator **

why using this line of code in python gives error:

required, *args, **kwargs = "Welcome to...", 1, 2, 3, site='stackoverflow.com'
                 ^
                 SyntaxError: invalid syntax

while using it in function signatures is ok like:

def function1(required, *args, **kwargs):
    pass

function1("Welcome to...", 1, 2, 3, site='stackoverflow.com')

Upvotes: 3

Views: 2129

Answers (3)

amcgregor
amcgregor

Reputation: 1244

required, *args, **kwargs = "Welcome to...", 1, 2, 3, site='stackoverflow.com'
                 ^

Star notation like this generally goes last in an expansion, especially functional invocation or function argument specification. Additionally, you can not assign to it in this way.

[*prefix, 2, 3, 4, *mid, 5, *suffix]

Literal forms of certain complex object types (such as list, above, and dictionary) permit expansion using star notation at any point in the expression, and to a degree function invocations do, too. In argument specification where you are declaring the desire for "unlimited information", these notations have additional meaning and consequence.

def foo(bar, *baz, diz): ...

diz is now name-only and can not be passed in positionally. Without a default value, it is now a required named argument. Because being able to declare arguments as keyword-only is actually quite useful, and accepting and stuffing additional positional arguments into an unused variable a bit of an anti-pattern, PEP 3102 — Keyword-Only Arguments was written up and accepted to formalize using just a *, bare, to specify "everything after this point is keyword only". Additionally, there is a / marker to denote the separation between positional-only, and positional-or-keyword: the reverse. Reference.

Boils down to: literal expansion notation and argument specification notation mean and do different things.

Upvotes: 3

S.B
S.B

Reputation: 16536

My guess is because if we want this **kwargs to work, we have to have keyword-argument on the right hand side(It should be converted to dictionary) just like you did :

required, *args, **kwargs = "Welcome to...", 1, 2, 3, site='stackoverflow.com'

But as we know right hand side of an assignment is evaluated fist(then the unpacking occurs). so in this case you can think of an another assignment in an expression which is invalid. for example :

a = (1, 2, 3)       # Valid
b = (1, 2, var=20)  # Invalid

By looking at:

from dis import dis
dis('a, b, c = 5, 4, 3')


  1           0 LOAD_CONST               0 ((5, 4, 3))
              2 UNPACK_SEQUENCE          3
              4 STORE_NAME               0 (a)
              6 STORE_NAME               1 (b)
              8 STORE_NAME               2 (c)
             10 LOAD_CONST               1 (None)
             12 RETURN_VALUE

Python tries to build a tuple from right hand side of the assignment, you can't have an assignment statement in there. I think same thing is happened and that's the reason.

Upvotes: 3

Patrik Valkovič
Patrik Valkovič

Reputation: 724

Python doesn't allow destructuring the same way as JavaScript does (I assume you have a background in it). The function signature is special, it allows a variadic number of parameters - without the syntax you would need to specify all the parameters and set them by default to None. The *args (for positional arguments) and **kargs (for keyword arguments) allows to work with all the parameters as the list (or dictionary in case of keyword arguments).

Python can destruct only tuples, so only the following code is valid.

first_param, second_param, third_param = "Welcome to...", 1, 2

You can even use one asterisk to substitute multiple values, but it can be there only once

first_param, *rest, last_param = "Welcome to...", 1, 2, 3

Also, you need to destruct the whole tuple, so the following code is not valid and will fail at runtime.

first_param, second_param = "Welcome to...", 1, 2

The site='stackoverflow.com' is possible only as keyword argument.

def function1(param1=1, param2=2):
    pass

function1(param2='hello')

This is the only case where you can use this syntax.

Upvotes: 3

Related Questions