[Tutor] Assigning variables with names set by other variables

Steven D'Aprano steve at pearwood.info
Fri Nov 4 23:45:35 CET 2011


Max S. wrote:
> Is it possible to create a variable with a string held by another variable
> in Python?  For example,

Yes, but you shouldn't do it. Seriously. Don't do this, you will regret it.

     var_name = input("Variable name? ")  # use raw_input in Python 2
     exec("%s = 4" % var_name)


Instead, you should use a dictionary, like this:

     var_name = input("Variable name? ")
     table = {var_name: 4}

and then later when you need to retrieve the value:

     print(table[var_name])



Why shouldn't you use exec?

Three main reasons:

(1) This code contains a MAJOR vulnerability to a code injection attack. 
   There are enough code injection vulnerabilities in the world without 
you adding to it, please don't add another.

(2) It makes for hard to read, difficult to follow code.

(3) It's slow.


If you don't know what code injection attacks means, consider this 
simple example where I create a variable spam=4 while executing any code 
I like:

 >>> var_name = input('Enter the variable name: ')
Enter the variable name: print(123*456); spam
 >>> exec("%s = 4" % var_name)
56088
 >>> spam
4


In this case, executing "print(123*456)" is harmless, but annoying, but 
it could do *anything* that Python can do (which is pretty much 
*anything at all*: delete files, send email, take over your computer, 
anything). Code injection attacks are among the two or three most common 
methods that viruses and malware operate.

Sanitising user input so it is safe to pass to exec is a hard job. But 
suppose you do it (somehow!):

     var_name = sanitise(input('Enter the variable name: '))
     exec("%s = 4" % var_name)
     # ...
     # ... later on
     # ...
     print(spam+1)  # do something useful with the new variable

But wait, that can't work! How do you know that the variable is called 
"spam"? You don't. It could be called anything. So now you have to do this:

     exec("print(%s+1)" % var_name)

which is a nuisance, it is harder to read and harder to follow, and 
defeats any of the useful features in your editor or IDE. It gets worse 
if you need to use this var_name repeatedly:

     exec("print(%s+1)" % var_name)
     exec("my_list = [1, 2, 3, %s, 5]" % var_name)
     print(my_list)
     exec("y = func(23, %s, 42) + %s" % (var_name, var_name))
     print(y)

How tedious and painful and hard to follow. And it is potentially buggy: 
what if the user typed "func" as the variable name, by accident? Or 
over-wrote one of your other variables?

And it's slow. Every time you call exec(), Python has to run a 
mini-interpreter over the string, analyzing it, splitting it into 
tokens, compiling it into code that can be executed, and then finally 
execute it. In general, this is slow: in my experience, running 
exec("command") is about 10 times slower than just running command directly.

So just avoid using exec. Anytime you think you need exec, you almost 
certainly do not. And you definitely don't need it for indirect 
variables! Just use a dictionary instead:

     var_name = input("Variable name? ")
     table = {var_name: 4}
     # ...
     # ... later on
     # ...
     print(table[var_name]+1)
     my_list = [1, 2, 3, table[var_name], 5]
     print(my_list)
     y = func(23, table[var_name], 42) + table[var_name]
     print(y)




-- 
Steven



More information about the Tutor mailing list