Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"ES6-like" Python dict spread [duplicate]

Tags:

python

I'm trying to write a function spread in Python 3.6 (I cannot use any newer release), and, so far, I've got something that looks like this:

d = {"a": 1, "b": 2, "c": 3}
a, b, c = spread(d, ['a', 'b', 'c'])
a
>> 1
b
>> 2
c
>> 3

The problem is: there is kind of duplication since the position of the left side must match the keys list on the function's 2nd argument for it to make sense. So, change the order of the keys list, and variable a will hold a different value than d['a']. I need to keep consistency by either

a, b, c = spread(d) # how?

Or spread(d, ???). I'm not considering initializing a, b, c with None and then pass them as a list.

Any thoughts or leads on how to approach this? Is it even possible? Thanks!

like image 460
Erico Avatar asked Jan 18 '19 16:01

Erico


4 Answers

No this isn't really possible. You can't have

a, b, c = spread(d)

and

a, c, b = spread(d)

give the same value to b. This is because the right side of an assignment statement is evaluated first. So spread executes and returns its values before your code knows which order you put them in on the left.

Some googling leads be to believe that by "spread-like syntax for dicts", you're looking for the **dict syntax. See What does ** (double star/asterisk) and * (star/asterisk) do for parameters?

like image 139
Patrick Haugh Avatar answered Oct 22 '22 21:10

Patrick Haugh


not very pretty, but you can sort of get there doing:

def f1(a, b, c, **_):
    print(a)
    print(b)
    print(c)

d = {"a": 1, "b": 2, "c": 3}

f1(**d)

very different semantics, but posted in the hope it'll inspire something!

as per @phhu's comment, ** in the definition of f1 is a catch-all keyword argument specifier telling Python that all unmatched parameters should be put into a dictionary of the given name, _ in my case. calling as f1(**d) says to unpack the specified dictionary into the function's parameters.

hence if it was used like:

e = {"a": 1, "b": 2, "c": 3, "extra": 42}

f1(**e)

then inside f1 the _ variable would be set to {"extra": 42}. I'm using _ because this identifier is used across a few languages to indicate a throwaway/placeholder variable name, i.e. something that is not expected to be used later.

like image 33
Sam Mason Avatar answered Oct 22 '22 21:10

Sam Mason


globals().update(d) does what you ask, but...

  • It works in the global scope only, locals() is not guaranteed to return a writable dictionary.
  • It impairs debuggability of your code. If one of the variables set this way ends up with an unexpected value, no search will show you that this is the place the variable is being set.
like image 42
jasonharper Avatar answered Oct 22 '22 19:10

jasonharper


You could assign the variables to the result of a values() call:

>>> d = {"a": 1, "b": 2, "c": 3}
>>> a,b,c = d.values()
>>> a
1
>>> b
2
>>> c
3

I don't recommend doing this for versions of Python where dict ordering is not guaranteed, but luckily this should work in 3.6 and above.

like image 1
Kevin Avatar answered Oct 22 '22 21:10

Kevin