14

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!

Erico
  • 629
  • 1
  • 6
  • 14
  • Could you add an example of what "spread" does in ES6? – Dani Mesejo Jan 18 '19 at 16:12
  • 2
    why not just access the values in the dictionary? no need to pollute the module name-space like this normally – Chris_Rands Jan 18 '19 at 16:12
  • Using `locals()` is actually a very interesting lead, thanks @Graipher. I don't know why your response was removed ): – Erico Jan 18 '19 at 16:31
  • @Chris_Rands I'm curious ;) – Erico Jan 18 '19 at 16:33
  • 1
    this seems to be a normative reference for javascript: [Object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) – Sam Mason Jan 18 '19 at 17:15
  • 1
    `dict` objects are not really drop in replacements for javascript objects. `dict` objects are more like the equivalent to Javascript Map objects. You can't achieve this in Python without hacky globals stuff, and for local assignments, it would require even more hacky stuff. – juanpa.arrivillaga Jan 18 '19 at 17:26

4 Answers4

7

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.

Sam Mason
  • 15,216
  • 1
  • 41
  • 60
  • just realised this was implied in other comments… does having this as a separate answer serve any purpose? – Sam Mason Jan 18 '19 at 17:19
  • Note (to self): the **_ in the function arguments serves to absorb any additional entries in the dictionary, preventing an "unexpected keyword argument" from occurring. – phhu Jun 16 '20 at 13:42
  • 1
    @phhu have tried to explain what's going on, hope it helps! – Sam Mason Jun 16 '20 at 14:27
6

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?

Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • yeah, I got to this same point, but it's unfortunate the `**` cannot be used starting the right-hand side of the operation ): – Erico Jan 18 '19 at 16:20
2

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.
jasonharper
  • 9,450
  • 2
  • 18
  • 42
  • Thanks! It's a good lead, I'll try to make my way around using it (: Another user gave me this same lead earlier, but his comment was removed, unfortunately ): – Erico Jan 18 '19 at 16:48
1

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.

Kevin
  • 74,910
  • 12
  • 133
  • 166
  • 1
    note that the guaranteed ordering is _insertion_ order, or the order defined in the literal here. If you want something different, like alphabetical order, sorting is needed – Felk Jan 18 '19 at 16:12
  • 1
    I wouldn't go for this option. Within the context I'm working, this just won't work, because I can never guarantee the upper layers will keep the expected order in an ordered dictionary. Thanks for your thoughts (: – Erico Jan 18 '19 at 16:16