57

I am studying Python and currently going through some more learning with dictionaries.

I was wondering;

If I have a dictionary like: d = {'key_1': 'value_a', 'key_2': 'value_b'} and I want separate/divide this dictionary into variables where each variable is a key from the dictionary and each variables value is the value of that key in the dictionary.

What would be the best pythonic way to achieve this?

d = {'key_1': 'value_a', 'key_2': 'value_b'}
#perform the command and get
key_1 = 'value_a'
key_2 = 'value_b'

I tried: key_1, key_2 = d but it did not work.

Basically I am seeking expert's wisdom to find out if there is a better way to reduce 2 lines of code into one.

Note: This is not a dynamic variable creation.

Phil
  • 13,875
  • 21
  • 81
  • 126
  • 1
    Dictionaries have no order which makes this a difficult task to create a general solution for, there is no need to do this anyway. If it was implemented it would be hacky. – jamylak Jun 22 '12 at 13:08
  • 4
    There's no pythonic way, because the very idea is non-pythonic. In python, you don't want to create variables dynamically. – georg Jun 22 '12 at 13:09
  • 4
    You shouldn't do that. The answer is `key_1, key_2 = d.values()`. but 1) this is prone to exceptions; 2) you won't know which is which, unless you sort `d.values()` before assignment. It's highly unlikely that you need all this. – Lev Levitsky Jun 22 '12 at 13:09
  • 2
    **duplicate** from : http://stackoverflow.com/questions/3803419/create-variables-from-strings-in-python – jlengrand Jun 22 '12 at 13:10
  • That said, you can easily "create" variables _in another function_ by passing `**dict` to it. – georg Jun 22 '12 at 13:11
  • Hello. I am not trying to create variables dynamically. I have a dictionary, I know its keys. Typing the key names in the code myself, I want dictionaries keys' values assigned to their respective variables. So I thought instead of typing 2 lines of code (1 for each key), there might be a one-line solution. And jlennotgrand, it is not a duplicate **at all**. – Phil Jun 22 '12 at 13:12
  • 12
    `key1, key2 = d['key1'], d['key2']`? – georg Jun 22 '12 at 13:15
  • thg435, that's exactly what I will do. Thank you. I just thought Python would magically do the distribution. It would be cool and useful if it did. ;-) – Phil Jun 22 '12 at 13:18
  • It will do it magically if you can stuff the logic that needs to do the unpacking and all the manipulation of the unpacked data in to a function. Then you just have `def func(**kwargs):` and call it like `func(**d)`. From within func scope, you'll have all your dict keys from d available as labels. – Silas Ray Jun 22 '12 at 13:23
  • In JavaScript, this is called "destructuring assignment": http://wiki.ecmascript.org/doku.php?id=harmony:destructuring - Python supports destructuring assignment, but only for iterables, not for `dict`s. – ecatmur Jun 22 '12 at 13:32
  • It indeed is its duplicate ecatmur, thank you for pointing that out. I did my research but missed it since I did not know the words destructuring assignment. – Phil Jun 22 '12 at 13:38
  • @ecatmur A dictionary is an iterable, hence why you can do `for x in d:` etc. – Silas Ray Jun 22 '12 at 13:39
  • @sr2222 true, better to say that Python destructuring assignment (variable unpacking) doesn't have support for mapping types, so `dict` is treated as an iterable. – ecatmur Jun 22 '12 at 13:44

10 Answers10

75

The existing answers will work, but they're all essentially re-implementing a function that already exists in the Python standard library: operator.itemgetter()

From the docs:

Return a callable object that fetches item from its operand using the operand’s __getitem__() method. If multiple items are specified, returns a tuple of lookup values. For example:

After f = itemgetter(2), the call f(r) returns r[2].

After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3]).


In other words, your destructured dict assignment becomes something like:

from operator import itemgetter

d = {'key_1': 'value_a', 'key_2': 'value_b'}
key_1, key_2 = itemgetter('key_1', 'key_2')(d)

# prints "Key 1: value_a, Key 2: value_b"
print("Key 1: {}, Key 2: {}".format(key_1, key_2))
mintchkin
  • 1,564
  • 2
  • 14
  • 13
28

Problem is that dicts are unordered, so you can't use simple unpacking of d.values(). You could of course first sort the dict by key, then unpack the values:

# Note: in python 3, items() functions as iteritems() did
#       in older versions of Python; use it instead
ds = sorted(d.iteritems())
name0, name1, name2..., namen = [v[1] for v in ds]

You could also, at least within an object, do something like:

for k, v in dict.iteritems():
    setattr(self, k, v)

Additionally, as I mentioned in the comment above, if you can get all your logic that needs your unpacked dictionary as variables in to a function, you could do:

def func(**kwargs):
    # Do stuff with labeled args

func(**d)
Silas Ray
  • 25,682
  • 5
  • 48
  • 63
  • Thank you very much for actually reading my quesiton and responding. I guess I better go with the standart route of separating the dict into variables my self. I just thought there could be a one line solution without iterating or sorting, but I guess not. so I will do key = dict['key'] for each key. But thank you VERY MUCH for this. – Phil Jun 22 '12 at 13:16
  • 1
    If your usecase allows your source dictionary to be an `OrderedDict`, and you can predict the insertion order, you could skip the sort and just unpack `od.values()` directly. – Silas Ray Jun 22 '12 at 13:19
  • In this use case, it seems like an overkill but thank you. You thought me something. values thing looks very useful. – Phil Jun 22 '12 at 13:22
  • 1
    The first snippet can be simplified down to `name1... = (d[k] for k in sorted(d))` – georg Jun 22 '12 at 13:29
  • @thg435 True, but it is less explicit that way. There's implicit understanding in that that when you use a dictionary syntactically as a simple iterable, it behaves like a set of the keys. I think using `iteritems()` makes it more illustrative, even if it is less concise. – Silas Ray Jun 22 '12 at 13:35
  • @sr2222: up to you, but the lambda is still superfluous (this is the default behavior). – georg Jun 22 '12 at 13:41
  • @thg435 Fair enough. Nuked. :) – Silas Ray Jun 22 '12 at 13:44
  • Hope Python will get with the program...JavaScript now has key-value destructuring and I use it everywhere. – Andy Feb 17 '17 at 17:02
28

A solution which has not been mentionned before would be

dictget = lambda d, *k: [d[i] for i in k]

and then use it:

key_1, key_2 = dictget(d, 'key_1', 'key_2')

whose advantage is that it is quite readable even with more variables to be retrieved.

Even more readable, however, would be a "real" function such as

def dictget(d, *k):
    """Get the values corresponding to the given keys in the provided dict."""
    return [d[i] for i in k]
    # or maybe
    return (d[i] for i in k) # if we suppose that we have bigger sets of result
    # or, equivalent to this
    for i in k:
        yield d[i]

which as well supports commenting with a docstring and is to be preferred.

glglgl
  • 89,107
  • 13
  • 149
  • 217
6
var1, var2 = (lambda key1, key2: (key1, key2))(**d)

If you want to give anyone reading your code a headache you can use anonymous function to unpack values like this.

Devilholk
  • 61
  • 1
  • 1
5

You can do this, if you're brave:

for k, v in d.items():
    locals()[k] = v

But being brave might not be enough - you might also have to be reckless etc.

If you want to be a reckless hero like @ecatmur, you can do this:

locals().update(d)

But now that OP has updated his question and answered comments, it seems, this isn't what he really wants to do. Just for the record: There are good reasons for dynamically creating variables - even if everyone here agrees that it's not pythonic. A lot of interpreter style problems can be solved neetly with dynamic altering of your scope. Just do this in a controlled fashion. And... uh, don't deploy this to some production site ;)

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
  • 5
    btw, don't ever do that - per http://docs.python.org/library/functions.html#locals *The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.* – ecatmur Jun 22 '12 at 13:30
  • 1
    Moreover, this just plain doesn't work; if you do something like `print([k for k in locals()])`, you'll see the values you added from d, but if you actually try to access them by name, you'll still get an error: `name '' is not defined`. I certainly understand why this should never be used in production code, but it would sure be nice if it worked for prototyping. – K. Nielson Mar 21 '19 at 15:06
  • Note: This in CPython only happens to work in global scope where `locals()` and `globals()` are the same. This does not work inside functions. – MegaIng Nov 05 '21 at 20:06
3

i think this should solve your problem

d = {'key_1': 'value_a', 'key_2': 'value_b'}
for k,v in d.items():
    exec '%s=%s'%(k,v)
Tanu
  • 237
  • 2
  • 7
  • 1
    This works for values that are not necessarily strings: `for k in my_dict.keys(): exec "{0}=my_dict[\'{0}\']".format(k)` – Marc Shivers Jan 07 '14 at 19:33
2

I actually have a usecase, where i pull all the arguments of an __init__ method into the self namespace on object construction:

vars(self).update(somedict)

The vars function gets you a dict of the “namespace” associated with the object passed. However, this will not work with locals in a function, due to CPython implementation details. So it's not supposed to work on all interpreters.

For global namespace you would substitute vars(self) with globals(), but this is really a sign that something is wrong with your logic. As said, for local variables this won't work anyways (It'll NameError even if you assigned a value to the dict).

Jonas Schäfer
  • 20,140
  • 5
  • 55
  • 69
1

@glglgl's answer is not most voted, that answer worked for me,

solution1={'variable': np.array([75, 74]), 'function': 0}

def dict_get(d, *k):
    for i in k:
        yield d[i]


variables, obj_func = dict_get(solution1, 'variable', 'function')

a, b=variables

print(a)

reference: @glglgl

itssoelif
  • 11
  • 4
0

It's not recommended to ever declare variables dynamically, because it becomes very difficult to find the source of a variable name. That said, it's possible to hack together a solution Dynamic variable name in python but I wouldn't recommend it. Probably better off using a plain old dictionary.

Community
  • 1
  • 1
Emmett Butler
  • 5,969
  • 2
  • 29
  • 47
  • Hello, I am not trying to dynamically create variables. I want to reduce n amount of lines (n is key count in the dict) to single line. I thought it would be possible with Python but now I guess not. – Phil Jun 22 '12 at 13:15
-1

Here is a solution that uses Python's inspect module on the calling stack frame to determine how to extract the right values from a supplied dictionary. Right now the only check is to make sure that there is a value available for each of the output variables, but feel free to add additional checks if you need them.

import inspect
def update(src):
    result = []
    cprev = inspect.currentframe().f_back
    #print(cprev.f_code.co_names)
    for v in cprev.f_code.co_names[1:]:
        if src is cprev.f_locals[v]: continue
        if v in src:
            result.append(src[v])
        else:
            raise NameError(v + " has no source for update")
    return result

Usage looks like this:

src={'a': 1, 'b': 2, 'c': 3}
a,b,c = update(src)