ChatGPT解决这个技术问题 Extra ChatGPT

How do I create variable variables?

How do I create the equivalent of PHP variable variable names in Python?

I hear this is a bad idea, in general, though. Is that true?

it's the maintainance and debugging aspects that cause the horror. Imagine trying to find out where variable 'foo' changed when there's no place in your code where you actually change 'foo'. Imagine further that it's someone else's code that you have to maintain... OK, you can go to your happy place now.
A further pitfall that hasn't been mentioned so far is if such a dynamically-created variable has the same name as a variable used in your logic. You essentially open up your software as a hostage to the input it is given.
You can modify your global and local variables by accessing the underlying dictionaries for them; it's a horrible idea from a maintenance perspective ... but it can be done via globals().update() and locals().update() (or by saving the dict reference from either of those and using it like any other dictionary). NOT RECOMMENDED ... but you should know that it's possible.
@JimDennis actually, no it can't. Modifications to the dict returned by locals will not affect local namespaces in CPython. Which is another reason not to do it.
@juanpa.arrivillaga: I had tried testing this in an IPython shell, but did so at the top level (where locals() behaves like globsls()). Redoing that test within a nested code (within the definition of a function) does show that I can't modify locals() from within that. As you say, the help for locals (3.7.6) does warn: "NOTE: Whether or not updates to this dictionary will affect name lookups in the local scope and vice-versa is implementation dependent and not covered by any backwards compatibility guarantees."

w
wjandrea

You can use dictionaries to accomplish this. Dictionaries are stores of keys and values.

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2

You can use variable key names to achieve the effect of variable variables without the security risk.

>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'

For cases where you're thinking of doing something like

var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

a list may be more appropriate than a dict. A list represents an ordered sequence of objects, with integer indices:

lst = ['foo', 'bar', 'baz']
print(lst[1])           # prints bar, because indices start at 0
lst.append('potatoes')  # lst is now ['foo', 'bar', 'baz', 'potatoes']

For ordered sequences, lists are more convenient than dicts with integer keys, because lists support iteration in index order, slicing, append, and other operations that would require awkward key management with a dict.


d
davidism

Use the built-in getattr function to get an attribute on an object by name. Modify the name as needed.

obj.spam = 'eggs'
name = 'spam'
getattr(obj, name)  # returns 'eggs'

v
vallentin

It's not a good idea. If you are accessing a global variable you can use globals().

>>> a = 10
>>> globals()['a']
10

If you want to access a variable in the local scope you can use locals(), but you cannot assign values to the returned dict.

A better solution is to use getattr or store your variables in a dictionary and then access them by name.


locals().update({'new_local_var':'some local value'}) works just fine for me in Python 3.7.6; so I'm not sure what you mean when you say you cannot assign values through it.
Given x = "foo" and locals()["x"] = "bar" using print x gives the output bar for Jython 2.5.2. This was tested with an On Demand Automation Script in maximo.
The documentation of locals() specifically says: "The contents of this dictionary should not be modified." (emphasis mine)
@JimDennis`locals()`` provides a dictionary created to represent local variables. Updating it does not guarantee to update the actual local variables. In modern Python implementations it's more like a picture (showing the content) in a nice frame (a high-level dict) – drawing on the picture won't actually change the real thing.
The reason it doesn't work, at least on CPython, is that CPython allocates a fixed size array for locals, and the size of said array is determined when the function is defined, not when its run, and can't be changed (access to true locals doesn't even use the name; the name is replaced with the index into the array at function compile time). locals() returns a true dict; within a function, that dict is made by loading names and associated values in the array when you call locals(), it won't see future changes. If it changes, you're at global or class scope (which use dict scopes).
T
TigerhawkT3

New coders sometimes write code like this:

my_calculator.button_0 = tkinter.Button(root, text=0)
my_calculator.button_1 = tkinter.Button(root, text=1)
my_calculator.button_2 = tkinter.Button(root, text=2)
...

The coder is then left with a pile of named variables, with a coding effort of O(m * n), where m is the number of named variables and n is the number of times that group of variables needs to be accessed (including creation). The more astute beginner observes that the only difference in each of those lines is a number that changes based on a rule, and decides to use a loop. However, they get stuck on how to dynamically create those variable names, and may try something like this:

for i in range(10):
    my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)

They soon find that this does not work.

If the program requires arbitrary variable "names," a dictionary is the best choice, as explained in other answers. However, if you're simply trying to create many variables and you don't mind referring to them with a sequence of integers, you're probably looking for a list. This is particularly true if your data are homogeneous, such as daily temperature readings, weekly quiz scores, or a grid of graphical widgets.

This can be assembled as follows:

my_calculator.buttons = []
for i in range(10):
    my_calculator.buttons.append(tkinter.Button(root, text=i))

This list can also be created in one line with a comprehension:

my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]

The result in either case is a populated list, with the first element accessed with my_calculator.buttons[0], the next with my_calculator.buttons[1], and so on. The "base" variable name becomes the name of the list and the varying identifier is used to access it.

Finally, don't forget other data structures, such as the set - this is similar to a dictionary, except that each "name" doesn't have a value attached to it. If you simply need a "bag" of objects, this can be a great choice. Instead of something like this:

keyword_1 = 'apple'
keyword_2 = 'banana'

if query == keyword_1 or query == keyword_2:
    print('Match.')

You will have this:

keywords = {'apple', 'banana'}
if query in keywords:
    print('Match.')

Use a list for a sequence of similar objects, a set for an arbitrarily-ordered bag of objects, or a dict for a bag of names with associated values.


s
sepp2k

Whenever you want to use variable variables, it's probably better to use a dictionary. So instead of writing

$foo = "bar"
$$foo = "baz"

you write

mydict = {}
foo = "bar"
mydict[foo] = "baz"

This way you won't accidentally overwrite previously existing variables (which is the security aspect) and you can have different "namespaces".


R
Rocky Li

Use globals()

You can actually assign variables to global scope dynamically, for instance, if you want 10 variables that can be accessed on a global scope i_1, i_2 ... i_10:

for i in range(10):
    globals()['i_{}'.format(i)] = 'a'

This will assign 'a' to all of these 10 variables, of course you can change the value dynamically as well. All of these variables can be accessed now like other globally declared variable:

>>> i_5
'a'

G
Georgy

Instead of a dictionary you can also use namedtuple from the collections module, which makes access easier.

For example:

# using dictionary
variables = {}
variables["first"] = 34
variables["second"] = 45
print(variables["first"], variables["second"])

# using namedtuple
Variables = namedtuple('Variables', ['first', 'second'])
vars = Variables(34, 45)
print(vars.first, vars.second)

Keep in mind namedtuples are immutable so they're a bit different than simply dicts with dot notation. Having said that, both options promote good design principles and don't abuse the global namespace like half the answers in this thread do.
D
Demi-Lune

The SimpleNamespace class could be used to create new attributes with setattr, or subclass SimpleNamespace and create your own function to add new attribute names (variables).

from types import SimpleNamespace

variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a

G
Guillaume Lebreton

If you don't want to use any object, you can still use setattr() inside your current module:

import sys
current_module = module = sys.modules[__name__]  # i.e the "file" where your code is written
setattr(current_module, 'variable_name', 15)  # 15 is the value you assign to the var
print(variable_name)  # >>> 15, created from a string

This does not work with __dict__ variable however. I wonder if there is a general mechanism to create any global variable dynamically.
globals() can do this
A
Andriy Ivaneyko

You have to use globals() built in method to achieve that behaviour:

def var_of_var(k, v):
    globals()[k] = v

print variable_name # NameError: name 'variable_name' is not defined
some_name = 'variable_name'
globals()[some_name] = 123
print(variable_name) # 123

some_name = 'variable_name2'
var_of_var(some_name, 456)
print(variable_name2) # 456

C
Community

I'm am answering the question: How to get the value of a variable given its name in a string? which is closed as a duplicate with a link to this question.

If the variables in question are part of an object (part of a class for example) then some useful functions to achieve exactly that are hasattr, getattr, and setattr.

So for example you can have:

class Variables(object):
    def __init__(self):
        self.foo = "initial_variable"
    def create_new_var(self,name,value):
        setattr(self,name,value)
    def get_var(self,name):
        if hasattr(self,name):
            return getattr(self,name)
        else:
            raise("Class does not have a variable named: "+name)

Then you can do:

v = Variables()
v.get_var("foo")

"initial_variable"

v.create_new_var(v.foo,"is actually not initial")
v.initial_variable

"is actually not initial"


m
mOmOney
# Python 3.8.2 (default, Feb 26 2020, 02:56:10)

Variable variables in Python

"""
<?php
$a = 'hello';
$e = 'wow'
?>
<?php
$$a = 'world';
?>
<?php
echo "$a ${$a}\n";
echo "$a ${$a[1]}\n";
?>
<?php
echo "$a $hello";
?>
"""

a = 'hello'  #<?php $a = 'hello'; ?>
e = 'wow'   #<?php $e = 'wow'; ?>
vars()[a] = 'world' #<?php $$a = 'world'; ?>
print(a, vars()[a]) #<?php echo "$a ${$a}\n"; ?>
print(a, vars()[vars()['a'][1]]) #<?php echo "$a ${$a[1]}\n"; ?>
print(a, hello) #<?php echo "$a $hello"; ?>

Output:

hello world
hello wow
hello world

Using globals(), locals(), or vars() will produce the same results

# Python 3.8.2 (default, Feb 26 2020, 02:56:10)

#<?php $a = 'hello'; ?>
#<?php $e = 'wow'; ?>
#<?php $$a = 'world'; ?>
#<?php echo "$a ${$a}\n"; ?>
#<?php echo "$a ${$a[1]}\n"; ?>
#<?php echo "$a $hello"; ?>

print('locals():\n')
a = 'hello'
e = 'wow'
locals()[a] = 'world'
print(a, locals()[a])
print(a, locals()[locals()['a'][1]])
print(a, hello)

print('\n\nglobals():\n')
a = 'hello'
e = 'wow'
globals()[a] = 'world'
print(a, globals()[a])
print(a, globals()[globals()['a'][1]])
print(a, hello)

Output:

locals():

hello world
hello wow
hello world


globals():

hello world
hello wow
hello world

Bonus (creating variables from strings)

# Python 2.7.16 (default, Jul 13 2019, 16:01:51)
# [GCC 8.3.0] on linux2

Creating variables and unpacking tuple:

g = globals()
listB = []
for i in range(10):
    g["num%s" % i] = i ** 10
    listB.append("num{0}".format(i))

def printNum():
    print "Printing num0 to num9:"
    for i in range(10):
        print "num%s = " % i, 
        print g["num%s" % i]

printNum()

listA = []
for i in range(10):
    listA.append(i)

listA = tuple(listA)
print listA, '"Tuple to unpack"'

listB = str(str(listB).strip("[]").replace("'", "") + " = listA")

print listB

exec listB

printNum()

Output:

Printing num0 to num9:
num0 =  0
num1 =  1
num2 =  1024
num3 =  59049
num4 =  1048576
num5 =  9765625
num6 =  60466176
num7 =  282475249
num8 =  1073741824
num9 =  3486784401
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) "Tuple to unpack"
num0, num1, num2, num3, num4, num5, num6, num7, num8, num9 = listA
Printing num0 to num9:
num0 =  0
num1 =  1
num2 =  2
num3 =  3
num4 =  4
num5 =  5
num6 =  6
num7 =  7
num8 =  8
num9 =  9

H
Hzzkygcs

I have tried both in python 3.7.3, you can use either globals() or vars()

>>> food #Error
>>> milkshake #Error
>>> food="bread"
>>> drink="milkshake"
>>> globals()[food] = "strawberry flavor"
>>> vars()[drink] = "chocolate flavor"
>>> bread
'strawberry flavor'
>>> milkshake
'chocolate flavor'
>>> globals()[drink]
'chocolate flavor'
>>> vars()[food]
'strawberry flavor'

Reference:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-string-as-a-variable-name#post548936


D
DomTomCat

The consensus is to use a dictionary for this - see the other answers. This is a good idea for most cases, however, there are many aspects arising from this:

you'll yourself be responsible for this dictionary, including garbage collection (of in-dict variables) etc.

there's either no locality or globality for variable variables, it depends on the globality of the dictionary

if you want to rename a variable name, you'll have to do it manually

however, you are much more flexible, e.g. you can decide to overwrite existing variables or ... ... choose to implement const variables to raise an exception on overwriting for different types etc.

you can decide to overwrite existing variables or ...

... choose to implement const variables

to raise an exception on overwriting for different types

etc.

That said, I've implemented a variable variables manager-class which provides some of the above ideas. It works for python 2 and 3.

You'd use the class like this:

from variableVariablesManager import VariableVariablesManager

myVars = VariableVariablesManager()
myVars['test'] = 25
print(myVars['test'])

# define a const variable
myVars.defineConstVariable('myconst', 13)
try:
    myVars['myconst'] = 14 # <- this raises an error, since 'myconst' must not be changed
    print("not allowed")
except AttributeError as e:
    pass

# rename a variable
myVars.renameVariable('myconst', 'myconstOther')

# preserve locality
def testLocalVar():
    myVars = VariableVariablesManager()
    myVars['test'] = 13
    print("inside function myVars['test']:", myVars['test'])
testLocalVar()
print("outside function myVars['test']:", myVars['test'])

# define a global variable
myVars.defineGlobalVariable('globalVar', 12)
def testGlobalVar():
    myVars = VariableVariablesManager()
    print("inside function myVars['globalVar']:", myVars['globalVar'])
    myVars['globalVar'] = 13
    print("inside function myVars['globalVar'] (having been changed):", myVars['globalVar'])
testGlobalVar()
print("outside function myVars['globalVar']:", myVars['globalVar'])

If you wish to allow overwriting of variables with the same type only:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)

r
ru13r

Any set of variables can also be wrapped up in a class. "Variable" variables may be added to the class instance during runtime by directly accessing the built-in dictionary through __dict__ attribute.

The following code defines Variables class, which adds variables (in this case attributes) to its instance during the construction. Variable names are taken from a specified list (which, for example, could have been generated by program code):

# some list of variable names
L = ['a', 'b', 'c']

class Variables:
    def __init__(self, L):
        for item in L:
            self.__dict__[item] = 100

v = Variables(L)
print(v.a, v.b, v.c)
#will produce 100 100 100

R
Ruben Medrano

It should be extremely risky... but you can use exec():

a = 'b=5'
exec(a)
c = b*2
print (c)

Result: 10


This won't work inside a function. It's essentially equivalent to the safer locals()['b'] = 5 (which also won't work in a function).
@benrg Do you know how to get around Rubens failed suggestion because I'm stuck with the same situation? I have a file containing a long list of variable assignments as a collective string. I need to turn them into python assignments but eval() and exec() both fails.
k
kannappan

The setattr() method sets the value of the specified attribute of the specified object.

Syntax goes like this –

setattr(object, name, value)
Example –

setattr(self,id,123)

which is equivalent to self.id = 123

As you might have observed, setattr() expects an object to be passed along with the value to generate/modify a new attribute.

We can use setattr() with a workaround to be able to use within modules. Here’ how –

import sys
x = "pikachu"
value = 46
thismodule = sys.modules[__name__]
setattr(thismodule, x, value)
print(pikachu)