如何创建可变数量的变量?


363

如何在Python中完成变量变量?

例如,这是详尽的手动输入:变量变量

我听说这通常是个坏主意,这是Python中的一个安全漏洞。真的吗?


39
造成恐怖的原因是维护和调试方面。想象一下,当代码中没有实际更改“ foo”的地方时,试图找出变量“ foo”在何处更改。进一步想象一下,您必须维护其他人的代码...好吧,现在您可以去快乐的地方。
格琳·杰克曼(09年

4
到目前为止还没有提到的另一个陷阱是,如果这种动态创建的变量与您的逻辑中使用的变量具有相同的名称。本质上,您将软件作为输入的人质来打开。
holdenweb 2014年

此处的所有响应均假定您有权访问要按名称动态访问的基本变量,但情况并非总是如此。我认为在PHP中重现示例行为的最通用方法是使用eval(),如下所示:var_name ='foo'; bar = 5;输出= eval(var_name)
Luis Vazquez

1
您可以通过访问全局和局部变量的基础字典来修改它们;从维护的角度来看这是一个可怕的主意...但是可以通过globals()。update()locals()。update()来完成(或者通过保存其中的dict引用并像使用其他字典一样使用它) )。 不推荐 ...但是您应该知道这是可能的。
Jim Dennis

(我之所以添加此评论,是因为类似的问题(更具体地,要求此类操作的请求已通过指向此“足够接近”的问题的指针关闭了。我希望那些紧随其后的问题的人得到他们的答案)。
Jim Dennis

Answers:


296

您可以使用字典来完成此任务。字典是键和值的存储。

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

您可以使用变量键名来获得变量变量的效果,而不会带来安全风险。

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

对于您正在考虑做类似事情的情况

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

列表可能比字典更合适。一个列表代表对象的有序序列,并带有整数索引:

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

对于有序序列,列表比整数键类型的字典更方便,因为列表支持迭代的索引顺序,切片append和其他操作,将需要尴尬密钥管理与字典。


88

使用内置getattr函数按名称获取对象的属性。根据需要修改名称。

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

70

这不是一个好主意。如果要访问全局变量,则可以使用globals()

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

如果要访问本地作用域中的变量,可以使用locals(),但不能将值分配给返回的字典。

更好的解决方案是使用getattr变量或将其存储在字典中,然后按名称访问它们。


locals()。update({'new_local_var':'some local value'})在Python 3.7.6中对我来说很好;因此,当您说不能通过它分配值时,我不确定您的意思。
Jim Dennis

给定x = "foo"locals()["x"] = "bar"使用print x给出barJython 2.5.2 的输出。已使用maximo中的按需自动化脚本对此进行了测试。
传教士

37

每当您想使用变量变量时,最好使用字典。所以不要写

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

你写

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

这样,您就不会意外覆盖以前存在的变量(这是安全方面),并且您可以拥有不同的“命名空间”。


34

新编码员有时会编写如下代码:

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)
...

然后,给编码器留下一堆命名变量,编码工作量为O(m * n),其中m是命名变量的数量,n是需要访问变量组(包括创建)的次数。 )。更加精明的初学者注意到,这些行中的每行的唯一区别是根据规则而变化的数字,并决定使用循环。但是,他们陷入了如何动态创建这些变量名的困境,并可能尝试执行以下操作:

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

他们很快发现这是行不通的。

如果程序需要任意变量“名称”,则字典是最佳选择,如其他答案所述。但是,如果您只是尝试创建许多变量,而又不介意使用整数序列来引用它们,则可能是在寻找list。如果您的数据是同质的,例如每天的温度读数,每周的测验分数或图形小部件网格,则尤其如此。

可以如下组装:

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

list也可以用一个修真一行创建:

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

无论哪种情况,结果都是填充的list,第一个元素使用进行访问my_calculator.buttons[0],第二个元素使用进行访问my_calculator.buttons[1],依此类推。“基本”变量名称成为的名称,list并且使用可变标识符访问它。

最后,别忘了其他数据结构,例如set-类似于字典,只是每个“名称”都没有附加值。如果您只需要一个“袋子”的物品,这可能是一个不错的选择。代替这样的事情:

keyword_1 = 'apple'
keyword_2 = 'banana'

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

您将拥有:

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

将a list用于一系列相似的对象,将a set用于任意排序的对象袋,或将a dict用于具有关联值的名称袋。


12

除了字典之外,您还可以namedtuple在collections模块中使用它,这使访问更加容易。

例如:

# 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)

10

如果您不想使用任何对象,仍然可以setattr()在当前模块内部使用:

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

对我来说,这听起来比使用“ exec”更好。
fralau

但是,这不适用于__dict__变量。我想知道是否存在一种通用的机制来动态创建任何全局变量。
阿列克谢

globals()可以做到这一点
Guillaume Lebreton '18

9

SimpleNamespace类可用于创建新的属性setattr,或继承SimpleNamespace并创建自己的功能,增加新的属性名称(变量)。

from types import SimpleNamespace

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

6

我正在回答这个问题:如何在字符串中给定变量名的情况下获取变量的值? 该链接作为重复链接关闭,并带有指向该问题的链接。

如果所讨论的变量是一个对象(例如一个类的一部分)的一部分,那么一些有用的功能,以实现准确是hasattrgetattr,和setattr

因此,例如,您可以拥有:

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)

然后,您可以执行以下操作:

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

“初始变量”

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

“实际上不是最初的”


5

采用 globals()

实际上,您可以动态地将变量分配给全局范围,例如,如果要在全局范围内访问10个变量i_1i_2... i_10

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

这将为所有这10个变量分配一个“ a”,当然您也可以动态更改该值。现在可以像访问其他全局声明的变量一样访问所有这些变量:

>>> i_5
'a'

4

您必须使用globals()内置方法 来实现该行为:

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

3

共识是为此使用字典-参见其他答案。在大多数情况下,这是一个好主意,但是,由此产生了许多方面:

  • 您将自己负责此词典,包括垃圾收集(命令变量)等。
  • 变量变量既不存在局部性也不存在全局性,这取决于字典的全局性
  • 如果要重命名变量名,则必须手动进行
  • 但是,您要灵活得多,例如
    • 您可以决定覆盖现有变量或...
    • ...选择实现const变量
    • 对不同类型的覆盖提出例外
    • 等等

也就是说,我已经实现了变量变量管理器 -class,它提供了上述一些想法。它适用于python 2和3。

你会使用这个类是这样的:

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'])

如果只允许覆盖相同类型的变量:

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

乍一看,漫长的骆驼进口使我认为这是Java。
markroxor

3

我在python 3.7.3中都尝试过,可以使用globals()或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'


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


0

任何一组变量也可以包装在一个类中。通过在运行时通过__dict__属性直接访问内置字典,可以将“变量”变量添加到类实例中。

以下代码定义了Variables类,该类在构造过程中向其实例添加变量(在本例中为属性)。变量名来自指定的列表(例如,可能是由程序代码生成的):

# 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.