233

I want to create variables dynamically in Python. Does anyone have any creative means of doing this?

7
  • 3
    Can you explain "via to a while loop?" I have no idea what that means. Commented Feb 18, 2011 at 1:16
  • 2
    To solve what problem? Need some context. Commented Feb 18, 2011 at 1:16
  • I'm wanting to create a while loop and inside that loop have it create variables over and over until I tell it to stop. Does this make any sense? Commented Feb 18, 2011 at 1:18
  • 2
    what do you mean 'dynamically create variables?' Dynamically how? Can you just use a dictionary and assign various values of it? Commented Feb 18, 2011 at 1:23
  • 10
    @Trivikram: Incorrect, the variable scope is the entire function, not just the while loop. Commented Feb 18, 2011 at 1:24

8 Answers 8

213

Unless there is an overwhelming need to create a mess of variable names, I would just use a dictionary, where you can dynamically create the key names and associate a value to each.

a = {}
k = 0
while k < 10:
    # dynamically create key
    key = ...
    # calculate value
    value = ...
    a[key] = value 
    k += 1

There are also some interesting data structures in the collections module that might be applicable.

Sign up to request clarification or add additional context in comments.

12 Comments

@Josh and others... Creating an item (key,value) in a dictionary a isn't very different from creating the same in locals() or globals() that are implemented as dictionaries too. I wonder why I have been downvoted by some downvote sniper for my answer creating an item in globals() while the answer of Gintautas Miliauskas is upvoted and this answer that doesn't give the how-to for <dynamically create key> is upvoted 3 times.
@eyquem I did not give any specific way to create the key because I know nothing about the naming scheme the OP needs in practice. Instead I just gave the most generic and clean scheme possible to solve the problem.
@Josh You're right. The question is too short. I had the same problem to answer, and that's why I used random to create the names and the values
@eyquem local variables aren't implemented as dictionaries. Almost always, the locals() function creates a dictionary from the local variables but the dictionary doesn't exist unless you call locals().
@eyquem, the local namespace is implemented as slots on the stack so the bytecode can reference them directly as offsets in the stack frame (plus free variables which are also included when you call locals(). The exception is in Python 2.x when you use exec inside a function without specifying a local namespace for the exec. I'm not sure offhand where this is documented: it isn't part of the language definition because different implementations can choose differently how to implement the locals namespace.
|
131

globals() returns a dictionary of the module's variables. You can create a new variable by creating a key on that dictionary:

# By default, a module has some hidden variables defined
print({k: v for k, v in globals().items() if not k.startswith("__")})

for i in range(1, 11):
    globals()[f"my_variable_{i}"] = i

print()
print(my_variable_1)
print(my_variable_2)
# and so on

print()
print({k: v for k, v in globals().items() if not k.startswith("__")})

Result:

{}

1
2

{'i': 10, 'my_variable_1': 1, 'my_variable_2': 2, 'my_variable_3': 3, 'my_variable_4': 4, 'my_variable_5': 5, 'my_variable_6': 6, 'my_variable_7': 7, 'my_variable_8': 8, 'my_variable_9': 9, 'my_variable_10': 10}

18 Comments

The OP needs a way to create many values associated with many names. He worded that as "dynamically create variables," but using a dictionary is the right way to solve his real problem.
@Ned Batchelder Maybe the question is misguided. But I think it could be better for him to receive two answers: 1- yes you can do what you want 2- but the way you want to enter in isn't a godd one for this ans that reason. Doing so, the OP would learn two things: that Python is powerfull, and some advice to code correctly. On the contrary, if anybody is prevented to think by his own and to think about a problem, it is a restraint put on brain_storming and reflexion
I think the reason you are being downvoted is because spamming keys in to the globals dict is generally not considered a best practice. In fact in your own code example, what would you suppose happens if your random key generator were to generate a key that was already in the globals dict (for example the key "alphabet"). Also, I think it was implied by the OPs question that he would need to get his "variables" after they were created. How can he differentiate his dynamic variables from the ones that already existed in global scope using your solution? A separate dict is better in this case.
It's a good answer. I stumbled upon the answer with a legitimate need to do this, preventing me from having to write a nearly duplicated question.
It is worth pointing out that this answers provides the way to dynamically create global variables. This means that every variable defined on globals() will go on the module namespace and stay there until the end of the program. Most of the times when one says "variable" they mean a function local variable, which only exists in the context of the execution of a function. In this case globals() is likely not the solution to the problem. Consider explicitly using a dictionary, as in the answer provided by JoshAdel.
|
86

Use the exec() method to run arbitrary code. For example, say you have a dictionary and you want to turn each key into a variable with its original dictionary value, you can do the following:

>>> c = {"one": 1, "two": 2}
>>> for k, v in c.items():
...     exec(f"{k} = {v}")
... 
>>> one
1
>>> two
2

9 Comments

Yes, this would work, but I don't know why you would do this - remember Explicit is better than implicit, and "Readability Counts". This is just a bad idea IMHO.
@chris-piekarski Thanks for this answer. One reason for wanting to do such thing is when you need to communicate with a 3rd-party software that expects inputs passed as local variables, and at compile time you don't know the variables that it expects (I'm creating a Paraview plugin, for example, and it imposes this kind of constraints).
Be aware that this will not work in Python 3 within a function execution context. It still is fine in the top level (module) context. If you need to create a module variable, I would suggest editing the globals() dict, or calling setattr() on the module object.
Let me also underline that, in general, this poses a security risk when the keys or values are provided by external data (user input, a file or anything). The usual warnings when using eval/exec apply. You don't want someone to set the value to "send_me_all_your_private_data()" and get it executed on your machine.
Actually, this is a very good answer, and the only solution in specific circumstances. Say you have an array that represents an inventory. Say this inventory can have containers inside of it, and these containers has an inventory of its own, representing the items the container is holding. This is the only way that I know of to be able to store INDEXES in a string, and be able to change it, and access different indexes dynamically, e.g. if you change which container you are looking inside of.
|
39

Stuffing things into the global and/or local namespaces is not a good idea. Using a dict is so some-other-language-ish ... d['constant-key'] = value just looks awkward. Python is OO. In the words of a master: """Namespaces are one honking great idea -- let's do more of those!"""

Like this:

>>> class Record(object):
...     pass
...
>>> r = Record()
>>> r.foo = 'oof'
>>> setattr(r, 'bar', 'rab')
>>> r.foo
'oof'
>>> r.bar
'rab'
>>> names = 'id description price'.split()
>>> values = [666, 'duct tape', 3.45]
>>> s = Record()
>>> for name, value in zip(names, values):
...     setattr(s, name, value)
...
>>> s.__dict__ # If you are suffering from dict withdrawal symptoms
{'price': 3.45, 'id': 666, 'description': 'duct tape'}
>>>

6 Comments

I don't understand what you want to say. Why global and local namespaces are not good ideas ? Because "most namespaces are currently implemented as Python dictionaries" ? and that dictionaries are "some-other-language-ish" ? Why this criticism about dictionaries ? You think that the namespace of an instance is a better thing to use than a dictionary ? Well... do you know that:
"A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that's normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (functions such as abs(), and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. "
(docs.python.org/release/2.5/tut/…) The author is GvR himself, it's written in the 2.5 version, but not later, that's why I give this link
It seems there is no salvation outside dictionaries. Anyway, Python is based on objects and use mappings between the names and the objects everywhere, it's the data model, so it isn't possible to escape to that fact. That's what I think. Am I wrong ?
By the way: despite my argumentation, I prefer your solution. It's clearer ans simpler than the use of globals(). I like the use of setattr()
|
12
vars()['meta_anio_2012'] = 'translate'

2 Comments

This is the same as doing locals()['meta_anio_2012'] = 'translate', which won't work in a function and is specifically warned against in the documentation. It also suffers from many of the problems pointed out in the comments to other answers.
There is absolutely nothing wrong with using the vars() function to modify an object's variables, but you would need to update it slightly to use it to set the variables to a module (by passing in the module object). vars(sys.modules[__name__])['my_variable'] = 'value' There is nothing wrong with doing this as it fetches the underlying object's __dict__. The only time this may not work is if the object being fetched has write permissions on attributes.
8

Keyword parameters allow you to pass variables from one function to another. In this way you can use the key of a dictionary as a variable name (which can be populated in your while loop). The dictionary name just needs to be preceded by ** when it is called.

# create a dictionary
>>> kwargs = {}
# add a key of name and assign it a value, later we'll use this key as a variable
>>> kwargs['name'] = 'python'

# an example function to use the variable
>>> def print_name(name):
...   print name

# call the example function
>>> print_name(**kwargs)
python

Without **, kwargs is just a dictionary:

>>> print_name(kwargs)
{'name': 'python'}

Comments

4

NOTE: This should be considered a discussion rather than an actual answer.

An approximate approach is to operate __main__ in the module you want to create variables. For example there's a b.py:

#!/usr/bin/env python
# coding: utf-8


def set_vars():
    import __main__
    print '__main__', __main__
    __main__.B = 1

try:
    print B
except NameError as e:
    print e

set_vars()

print 'B: %s' % B

Running it would output

$ python b.py
name 'B' is not defined
__main__ <module '__main__' from 'b.py'>
B: 1

But this approach only works in a single module script, because the __main__ it import will always represent the module of the entry script being executed by python, this means that if b.py is involved by other code, the B variable will be created in the scope of the entry script instead of in b.py itself. Assume there is a script a.py:

#!/usr/bin/env python
# coding: utf-8

try:
    import b
except NameError as e:
    print e

print 'in a.py: B', B

Running it would output

$ python a.py
name 'B' is not defined
__main__ <module '__main__' from 'a.py'>
name 'B' is not defined
in a.py: B 1

Note that the __main__ is changed to 'a.py'.

1 Comment

My approach is.., new_var = eval('old_var' + str(count))
-1

For free-dom:

import random

alphabet = tuple('abcdefghijklmnopqrstuvwxyz')

globkeys = globals().keys()
globkeys.append('globkeys') # because name 'globkeys' is now also in globals()

print 'globkeys==',globkeys
print
print "globals().keys()==",globals().keys()

for i in xrange(8):
    globals()[''.join(random.sample(alphabet,random.randint(3,26)))] = random.choice(alphabet)
del i

newnames = [ x for x in globals().keys() if x not in globkeys ]
print
print 'newnames==',newnames

print
print "globals().keys()==",globals().keys()

print
print '\n'.join(repr((u,globals()[u])) for u in newnames)

Result

globkeys== ['__builtins__', 'alphabet', 'random', '__package__', '__name__', '__doc__', 'globkeys']

globals().keys()== ['__builtins__', 'alphabet', 'random', '__package__', '__name__', 'globkeys', '__doc__']

newnames== ['fztkebyrdwcigsmulnoaph', 'umkfcvztleoij', 'kbutmzfgpcdqanrivwsxly', 'lxzmaysuornvdpjqfetbchgik', 'wznptbyermclfdghqxjvki', 'lwg', 'vsolxgkz', 'yobtlkqh']

globals().keys()== ['fztkebyrdwcigsmulnoaph', 'umkfcvztleoij', 'newnames', 'kbutmzfgpcdqanrivwsxly', '__builtins__', 'alphabet', 'random', 'lxzmaysuornvdpjqfetbchgik', '__package__', 'wznptbyermclfdghqxjvki', 'lwg', 'x', 'vsolxgkz', '__name__', 'globkeys', '__doc__', 'yobtlkqh']

('fztkebyrdwcigsmulnoaph', 't')
('umkfcvztleoij', 'p')
('kbutmzfgpcdqanrivwsxly', 'a')
('lxzmaysuornvdpjqfetbchgik', 'n')
('wznptbyermclfdghqxjvki', 't')
('lwg', 'j')
('vsolxgkz', 'w')
('yobtlkqh', 'c')

Another way:

import random

pool_of_names = []
for i in xrange(1000):
    v = 'LXM'+str(random.randrange(10,100000))
    if v not in globals():
        pool_of_names.append(v)

alphabet = 'abcdefghijklmnopqrstuvwxyz' 

print 'globals().keys()==',globals().keys()

print
for j in xrange(8):
    globals()[pool_of_names[j]] = random.choice(alphabet)
newnames = pool_of_names[0:j+1]

print
print 'globals().keys()==',globals().keys()

print
print '\n'.join(repr((u,globals()[u])) for u in newnames)

result:

globals().keys()== ['__builtins__', 'alphabet', 'random', '__package__', 'i', 'v', '__name__', '__doc__', 'pool_of_names']


globals().keys()== ['LXM7646', 'random', 'newnames', 'LXM95826', 'pool_of_names', 'LXM66380', 'alphabet', 'LXM84070', '__package__', 'LXM8644', '__doc__', 'LXM33579', '__builtins__', '__name__', 'LXM58418', 'i', 'j', 'LXM24703', 'v']

('LXM66380', 'v')
('LXM7646', 'a')
('LXM8644', 'm')
('LXM24703', 'r')
('LXM58418', 'g')
('LXM84070', 'c')
('LXM95826', 'e')
('LXM33579', 'j')

10 Comments

-1: saying it twice doesn't make it a good idea. Modifying globals() is bad, and should only be done in very specialized circumstances, usually involving coding tools. If you need a dynamic set of named values, use a dictionary, it's what they're there for.
Why add to globals() just to have to turn around and keep a separate list of all the names you dumped in there? A dictionary will handle all of that for you, in a cleaner, simpler, more understandable, more flexible, and safer way. Just because you can do something doesn't mean you should do something.
@Ned Batchelder Downvoting twice doesn't make me undertsand more. I don't pretend that these two codes are perfect arguments: they are only answers to criticism made by free-dom on 2 points. They don't want to prove that it is good to use globals()['X'] to create a new object with name X that will be used as following: li.append(X) etc etc;
@Ned Batchelder Now, thanks to the answer and the CODE of John Machin, I know another manner: creating a new attribute in an instance with the name X, using setattr() : that's fine. Or I understand nothing. Until now, I wrote several codes, and John Machin also, and Duncan gave me precise explanations, while you only merely contented to downvote twice and to emit dogmatic sentence that Modifying globals() is bad Your downvotes begin to bore me and they don't make me understand
@Ned Batchelder Moreover, modifying globals() can't be bad in the absolute, since it IS modified when we just wrote v = 48 or for i in xrange(20) . I think that it's for a great part because writing globals()["v"] = 48 looks long and weird that it is badly considered. But where is the real weirdness in that ? I don'y understand.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.