20

I quite regularly want to create a dictionary where keys are variable names. For example if I have variables a and b I want to generate: {"a":a, "b":b} (typically to return data at the end of a function).

Are there any (ideally built in) ways in python to do this automatically? i.e to have a function such that create_dictionary(a,b) returns {"a":a, "b":b}

pfabri
  • 885
  • 1
  • 9
  • 25
kyrenia
  • 5,431
  • 9
  • 63
  • 93
  • `"a":a` doesn't make much sense, `a` is going to get replaced by its value. – Dimitris Fasarakis Hilliard Oct 02 '16 at 15:50
  • 1
    While there are certainly ways to do this (using `locals()`), I think this is very rarely a good solution. In the example you gave (returning multiple values from a function), consider returning a `namedtuple` from the collections module. – flornquake Oct 02 '16 at 16:00
  • 2
    @AnttiHaapala A place where one may use this regularly is in Django context data that gets passed to html templates. It makes sense to have `{"a":a, "b":b}` where the variable names in your views are the same as in the templates. – Daniel Holmes Sep 03 '19 at 10:25
  • 2
    Does this answer your question? [create dictionary from list of variables](https://stackoverflow.com/questions/9495262/create-dictionary-from-list-of-variables) – pfabri May 28 '20 at 15:04

5 Answers5

13

Have you considered creating a class? A class can be viewed as a wrapper for a dictionary.

# Generate some variables in the workspace
a = 9; b = ["hello", "world"]; c = (True, False)

# Define a new class and instantiate
class NewClass(object): pass
mydict = NewClass()

# Set attributes of the new class
mydict.a = a
mydict.b = b
mydict.c = c

# Print the dict form of the class
mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}

Or you could use the setattr function if you wanted to pass a list of variable names:

mydict = NewClass()
vars = ['a', 'b', 'c']
for v in vars: 
    setattr(mydict, v, eval(v)) 

mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}
p-robot
  • 4,652
  • 2
  • 29
  • 38
6

You can write your own function for create_dict

def create_dict(*args):
  return dict({i:eval(i) for i in args})

a = "yo"
b = 7
print (create_dict("a", "b"))

Which gives {'a': 'yo', 'b': 7} output.
Here's a simple generator for the same:

vars = ["a", "b"]
create_dict = {i:eval(i) for i in args}

or you can use this one-liner lambda function

create_dict = lambda *args: {i:eval(i) for i in args}
print (create_dict("a", "b"))

But if you want to pass the variables to the function instead of the variable name as string, then its pretty messy to actually get the name of the variable as a string. But if thats the case then you should probably try using locals(), vars(), globals() as used by Nf4r

Community
  • 1
  • 1
lycuid
  • 2,555
  • 1
  • 18
  • 28
  • 4
    ["Is using eval in Python a bad practice?"](http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice), usually `eval` should not be the go-to tool. – Ilja Everilä Oct 02 '16 at 16:44
  • its no problem in this case at all. – lycuid Oct 02 '16 at 16:56
  • Do note that **literal** eval will not even work, since names are not literals. – Ilja Everilä Oct 02 '16 at 17:02
  • you are right, it doesn't, maybe should stick with `eval` i guess. – lycuid Oct 02 '16 at 17:06
  • I don't think there is any problem to use `eval` at-least in the above case. – lycuid Oct 02 '16 at 17:06
  • Its really not great to say its "bad" to use it, I believe all languages (atleast the one that i use) are made with a really brilliant architecture, so to say that some function is not good to use, it wouldn't have been there in the first place, with all due respect I think everything, if used properly, is an asset in some way, so to say `eval` is dangerous, where I'll say its strong function to be used with care, that's all, its not bad. – lycuid Oct 02 '16 at 17:21
  • You proved yourself why `eval` is dangerous with the "use `literal_eval` here for less risk" shenanigans: it is hard to sanitize input to eval. It might prove safe in this **very** particular case, but people tend to throw it around like it's the next best thing since sliced bread, and at some point it will receive uncontrolled input. There are valid use cases for eval, but this is not one. OP should either use `locals()` or preferably rethink their approach entirely. So: no, `eval` in itself is not bad, but most of the recommendations to use it are. – Ilja Everilä Oct 02 '16 at 17:39
  • Not to mention that none of your examples work for function scope variables, which seemed to be what OP was after. – Ilja Everilä Oct 02 '16 at 17:44
3

Extending on the code of @Nf4r, I use something like:

a, b = 1, 2

def make_dict(*args):
    # Globals will change of size, so we need a copy
    g = {k: v for k, v in globals().items() if not k.startswith('__')}  

    result = {}
    for arg in args:
        for k, v in g.items():
            try:
                if v == arg:
                    result[k] = v
            except ValueError:
                continue  # objects that don't allow comparison

    return result

make_dict(a, b)
Roelant
  • 4,508
  • 1
  • 32
  • 62
  • Thanks for writing this solution here. It seems to be inefficient though as it searched globals. Is there any better solution that you may have come across? – toing Oct 03 '20 at 19:16
1

Have you tried something like:

a, b, c, d = 1, 2, 3, 4
dt = {k:v for k, v in locals().items() if not k.startswith('__')}
print(dt)
{'a': 1, 'd': 4, 'b': 2, 'c': 3}
Nf4r
  • 1,390
  • 1
  • 7
  • 9
0

Seems to be a duplicate of Python variables as keys to dict

I think the simplest version might be:

fruit_dict = dict(apple=apple, banana=banana, carrot=carrot) 
dijonkitchen
  • 130
  • 3
  • 9