Snargs : triple-star packing/unpacking of self-named arguments (and alternative to PEP 736)

Maybe a syntax like {a: *, b: 2, c: *} would make it intuitive, as if a is a very long name, there is no need to write it twice, whilst still getting a SyntaxError if we do {a: , b: 2, c: *}.

1 Like

I like this direction. It would ideally be intuitive given:

a, *_ = b

Maybe:

{'a': a}, **_ = mydict

But this doesn’t solve “this”. Also:

{'a': a, 'b': b}, **_ = mydict
# or?
{'a': a}, {'b': b}, **_ = mydict

So idk…

But to address what OP is trying to achieve it would look like:

a, b, **_ = mydict

So say this is a good idea:

a = 1
mydict = {:a, 'b': 2, 'c': 3}
print(mydict)  # {'a': 1, 'b': 2, 'c': 3}

Then above together with a, *var = seq:

{:a, 'b': b}, **r = mydict

print(a)   # 1
print(b)   # 2
print(r)   # {'c': 3}

{:d}, **_ = mydict  # KeyError

After all, I think @Nineteendo’s idea is more appropriate (correct):

{: a, 'b': b, **r} = mydict

I think @JoBe’s suggestion is an improvement in readability

{a : *, 'b': b, **r} = mydict

because when using * as a placeholder symbol, there’s at least a symbol to tell you there’s a placeholder.
: a looks a bit too sparse.
But thatt might be impossible to parse, which would force you to pivot to

{*:a, 'b': b, **r} = mydict

which again doesn’t look great to me.


or

{'a':*, 'b': b, **r} = mydict

which would handicap the IDEs quite horribly.

1 Like

Yes, this is true, but it does not clearly assigns the dict values to local names, it creates another dict.

The main interest IMO is to pass variables consistently between namespaces, with no place for confusion. And this can be made possible by self-named packing and unpacking methods (which would bring this (un/)packing possibilities by themselves independently, as an addition).

The point of assigning a different value to b does not matter so much IMO.
But probably the OP notation is not really consistent with current convention so another version could be :

***(a, b, _) = mydict

which is maybe more comprehensive alongside mydict = ***(a, b, c) (as proposed in OP).

→ It is actually special within a match statement, that is why I have chosen that character at first.

Of course, if one operator acting in the rhs (right-hand-side) of an assignment as a self-reference to the lhs, it could provide all these possibilities, but I think it has already been discussed in the threads around PEP 736 and was mainly critiqued for the poor readability.

Imo, *** Syntax might be quite confusing, but if ther shouldn’t be any new Syntax, a built-in function, or an overload of dict might be the way to go. The overload would only accept Keys, and throw a Name error if the object the name is referencing does not exist in the current scope. If we then want to add values and keys to the dict, we could still do that, as dicts are unordered, like:

a = ...
b = ...
c = ...
my_dict = dict(a, b, c) # Overload gets called
my_dict += {"d": 100}

Allowing unpacked arguments as the content of a new container is crossing the line for me. Should dict(a) still result in a copy of a, or should it produce {'a':a}? There is an abstraction collision, that will result in bugs, or at least confusion. JS did this in the past, and now Array.from exist. Don’t.

2 Likes