102

Is there a Pythonic way to assign the values of a dictionary to its keys, in order to convert the dictionary entries into variables? I tried this out:

>>> d = {'a':1, 'b':2}
>>> for key,val in d.items():
        exec('exec(key)=val')
            
        exec(key)=val
                 ^ 
        SyntaxError: invalid syntax

I am certain that the key-value pairs are correct because they were previously defined as variables by me before. I then stored these variables in a dictionary (as key-value pairs) and would like to reuse them in a different function. I could just define them all over again in the new function, but because I may have a dictionary with about 20 entries, I thought there may be a more efficient way of doing this.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
HappyPy
  • 9,839
  • 13
  • 46
  • 68
  • 14
    [Keep data out of your variable names](http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html). –  Aug 06 '13 at 21:19
  • 4
    It is not a good idea to create variables dynamically. – Ashwini Chaudhary Aug 06 '13 at 21:19
  • 1
    @Zero Piraeus - My question relates to entries created by me and not by a different user, so it's slighlty different from the question posted in the post you mentioned – HappyPy Aug 06 '13 at 21:51
  • 1
    @AshwiniChaudhary why not? – Charlie Parker Oct 16 '17 at 22:09
  • See also [Elegant way to unpack limited dict values into local variables in Python](https://stackoverflow.com/questions/17755178/elegant-way-to-unpack-limited-dict-values-into-local-variables-in-python) – ggorlen Nov 28 '21 at 16:05
  • @ggorlen I deduced that they are essentially the same question, and that the other version, although much less popular, is higher quality. – Karl Knechtel Jun 03 '22 at 19:05

7 Answers7

178

You can do it in a single line with:

>>> d = {'a': 1, 'b': 2}
>>> locals().update(d)
>>> a
1

However, you should be careful with how Python may optimize locals/globals access when using this trick.

Note

I think editing locals() like that is generally a bad idea. If you think globals() is a better alternative, think it twice! :-D

Instead, I would rather always use a namespace.

With Python 3 you can:

>>> from types import SimpleNamespace    
>>> d = {'a': 1, 'b': 2}
>>> n = SimpleNamespace(**d)
>>> n.a
1

If you are stuck with Python 2 or if you need to use some features missing in types.SimpleNamespace, you can also:

>>> from argparse import Namespace    
>>> d = {'a': 1, 'b': 2}
>>> n = Namespace(**d)
>>> n.a
1

If you are not expecting to modify your data, you may as well consider using collections.namedtuple, also available in Python 3.

Peque
  • 13,638
  • 11
  • 69
  • 105
61

This was what I was looking for:

>>> d = {'a':1, 'b':2}
>>> for key,val in d.items():
        exec(key + '=val')
HappyPy
  • 9,839
  • 13
  • 46
  • 68
  • 5
    `list(map(exec, ("{0}={1}".format(x[0],x[1]) for x in d.items())))` – belteshazzar Apr 17 '16 at 10:50
  • 2
    @dbliss `for k,v in d.items(): exec(k+'=v')` – nat chouf Feb 02 '18 at 09:42
  • Sweet answer, saves me having to declare a boatload of mongodb variables. @dblis you can just do `for key,val in dp.items(): exec(key + '=val')` That's one line in my book. – ajsp Jul 22 '18 at 13:36
  • 8
    Careful with eval, your dict might have `import shutil;shutil.rmtree('/')` or worse in it. – bugmenot123 Jun 26 '19 at 08:49
  • 3
    Should be downvoted. No need to use exec, see locals answers elsewhere. – Gringo Suave Dec 24 '20 at 03:45
  • @GringoSuave, which answer you are referring to?.. – Kirby Jan 14 '22 at 22:13
  • @Kirby, top answer currently by Peque. Fundamental idea should be avoided, namespace or dict best, followed by locals() as least bad options. Exec() probably the absolute last resort. – Gringo Suave Jan 18 '22 at 17:17
  • @HappyPy I have a problem with your implementation. If I try to use the variables later on outside the for loop, i.e. `my_new_var=str(c)`, I get `NameError: name 'c' is not defined`. How did you used them so? Thanks – abu Feb 25 '22 at 09:22
  • This solution does not work inside a function, but only if the variables are global – divenex Jul 14 '22 at 15:11
33

You already have a perfectly good dictionary. Just use that. If you know what the keys are going to be, and you're absolutely sure this is a reasonable idea, you can do something like

a, b = d['a'], d['b']

but most of the time, you should just use the dictionary. (If using the dictionary is awkward, you are probably not organizing your data well; ask for help reorganizing it.)

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    Thanks for your answer. Yes, I'm sure what the keys will be because I created them, and just want to use exactly the same variable names elsewhere. I was just wondering whether there's a more automatic way to do it, as I may have over 20 entries. – HappyPy Aug 06 '13 at 22:00
  • 7
    @user2635863: You might be looking for a [class](http://docs.python.org/2/tutorial/classes.html). If you have 20 variables that all need saving, you have a problem. – user2357112 Aug 06 '13 at 22:02
  • @HappyPy you shouldn't need 20+ named variables, the logic should allow you to unpack the dictionary directly in the other function (for example, your 20+ variables could be named arguments of a function) – Iuri Guilherme Dec 04 '22 at 02:03
21

you can use operator.itemgetter

>>> from operator import itemgetter
>>> d = {'a':1, 'b':2}
>>> a, b = itemgetter('a', 'b')(d)
>>> a
1
>>> b
2
suhailvs
  • 20,182
  • 14
  • 100
  • 98
  • This is the Pythonic way to do destructure dicts. Though not as shorthand as Javascript it's very easy to understand for the reader what's going on and does not mix data and variable names. – Hans Bouwmeester Feb 25 '22 at 17:38
  • 1
    Good one, but unfortunately `foobar, foobaz = itemgetter("foobar", "foobaz")(dictionary)` is not so attractive. Everything looks pretty good with single-letter variable names. Plus there's an import per script for something you might want to do only once or twice. – ggorlen Jun 11 '22 at 15:54
  • 1
    How would this be easier than unpacking 20+ named variables as is the case with OP's problem? You can do `itemgetter(*d.keys())(d)` but you'd have to type each one named variable on the left side anyway – Iuri Guilherme Dec 04 '22 at 02:06
13

Consider the "Bunch" solution in Python: load variables in a dict into namespace. Your variables end up as part of a new object, not locals, but you can treat them as variables instead of dict entries.

class Bunch(object):
    def __init__(self, adict):
        self.__dict__.update(adict)

d = {'a':1, 'b':2}
vars = Bunch(d)
print vars.a, vars.b
arogachev
  • 33,150
  • 7
  • 114
  • 117
tdelaney
  • 73,364
  • 6
  • 83
  • 116
5

Python has great support for list unpacking, but not dict or object unpacking. The most unsurprising and Pythonic approach seems to be accessing each item by hand to build an intermediate tuple as described in this answer:

a, b = d['a'], d['b']

However, if you have a lot of properties, or variable names are long, it can get nasty to do:

great, wow, awesome = dictionary['great'], dictionary['wow'], dictionary['awesome']

For context, the JavaScript equivalent of the above (destructuring) is:

const {great, wow, awesome} = dictionary;

Here's an option that is a bit more dynamic:

>>> dictionary = dict(great=0, wow=1, awesome=2)
>>> great, wow, awesome = (dictionary[k] for k in ("great", "wow", "awesome"))
>>> great
0
>>> awesome
2

This is still verbose; you could write a function to abstract things a bit, but unfortunately you still have to type everything twice:

>>> def unpack(dct, *keys):
...     return (dct[k] for k in keys)
... 
>>> dictionary = dict(great=0, wow=1, awesome=2)
>>> great, wow, awesome = unpack(dictionary, "great", "wow", "awesome")

You can generalize this to work on objects too:

>>> def unpack(x, *keys):
...     if isinstance(x, dict):
...         return (x[k] for k in keys)
...     return (getattr(x, k) for k in keys)
...
>>> from collections import namedtuple
>>> Foo = namedtuple("Foo", "a b c d e")
>>> foo = Foo(a=0, b=1, c=2, d=3, e=4)
>>> c, b, d, a = unpack(foo, "c", "b", "d", "a")
>>> d
3

After all is said and done, unpacking by hand on multiple lines is probably best for real production code that you need to be safe and comprehensible:

>>> great = dictionary["great"]
>>> wow = dictionary["wow"]
>>> awesome = dictionary["awesome"]
ggorlen
  • 44,755
  • 7
  • 76
  • 106
1

Use pandas:

import pandas as pd
var=pd.Series({'a':1, 'b':2})
#update both keys and variables
var.a=3
print(var.a,var['a'])
restrepo
  • 479
  • 3
  • 14
  • 10
    i love pandas, but this might be overkill (requiring pandas just so you get variables as an accesor). – Manuel G Mar 15 '15 at 02:15
  • 1
    Exactly what I needed. I have a dict where the values are pandas dataframes. So made total sense to use this solution. Thank you! – RajeshM Nov 24 '18 at 20:51