关于python:如何创建可变数量的变量?

How do I create a variable number of variables?

如何在Python中完成变量?

这里有一个详细的手工输入,例如:变量

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


您可以使用字典来完成这项工作。字典是键和值的存储。

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

您可以使用变量键名来实现变量的效果,而不存在安全风险。

1
2
3
4
>>> x ="spam"
>>> z = {x:"eggs"}
>>> z["spam"]
'eggs'

如果你想做类似的事情

1
2
3
4
var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

列表可能比dict更合适。列表表示具有整数索引的有序对象序列:

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

对于有序序列,列表比带整数键的dict更方便,因为列表支持索引顺序、切片、append和其他需要对dict进行笨拙的密钥管理的操作。


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

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


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

1
2
3
>>> a = 10
>>> globals()['a']
10

如果要访问本地作用域中的变量,可以使用locals(),但不能为返回的dict赋值。

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


每当你想使用变量时,最好使用字典。所以不是写作

1
2
$foo ="bar"
$$foo ="baz"

你写

1
2
3
mydict = {}
foo ="bar"
mydict[foo] ="baz"

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


新的编码人员有时会这样编写代码:

1
2
3
4
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是需要访问(包括创建)一组变量的次数。更精明的初学者注意到每一行的唯一区别是一个根据规则变化的数字,并决定使用循环。但是,他们会陷入如何动态创建这些变量名的困境,可能会尝试如下操作:

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

他们很快发现这行不通。

如果程序需要任意变量"名称",字典是最好的选择,如其他答案所述。但是,如果你只是想创建许多变量,而不介意用一个整数序列来引用它们,那么你可能在寻找一个list。如果您的数据是相同的,例如每日温度读数、每周测验分数或图形小部件网格,则情况尤其如此。

可按如下方式组装:

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

这个list也可以在一行中创建,理解如下:

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

这两种情况下的结果都是填充的list,第一个元素通过my_calculator.buttons[0]访问,第二个元素通过my_calculator.buttons[1]访问,依此类推。"base"变量名成为EDOCX1的名称(0),并使用不同的标识符访问它。

最后,不要忘记其他数据结构,如set--这类似于字典,只是每个"名称"没有附加值。如果你只需要一袋物品,这是一个很好的选择。而不是像这样:

1
2
3
4
5
keyword_1 = 'apple'
keyword_2 = 'banana'

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

您将获得:

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

使用list表示一系列相似的对象,使用set表示任意排列的一袋对象,或使用dict表示一袋具有相关值的名称。


除了字典,您还可以使用Collections模块中的namedtuple,这使得访问更加容易。

例如:

1
2
3
4
5
6
7
8
9
10
# 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)


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

1
2
3
4
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


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

1
2
3
4
5
6
7
8
9
10
11
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

SimpleNamespace类可用于创建带setattr的新属性,或子类SimpleNamespace并创建自己的函数来添加新的属性名(变量)。

1
2
3
4
5
6
7
from types import SimpleNamespace

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

使用globals()

实际上,您可以动态地将变量分配给全局作用域,例如,如果您希望在全局作用域i_1i_2上访问10个变量…i_10

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

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

1
2
>>> i_5
'a'

我要回答的问题是:如何在字符串中获取给定变量名的值?它作为副本关闭,并带有指向此问题的链接。

如果所讨论的变量是对象的一部分(例如,类的一部分),那么要精确实现的一些有用函数是hasattrgetattrsetattr

例如,您可以有:

1
2
3
4
5
6
7
8
9
10
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)

然后你可以做:

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

"initial_variable"

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

"is actually not initial"


大家的共识是用字典来解释这个问题——见其他答案。对于大多数情况来说,这是一个好主意,但是,由此产生的许多方面:

  • 您将自己负责这个字典,包括垃圾收集(在dict变量中)等。
  • 变量既没有局部性也没有全局性,这取决于字典的全局性。
  • 如果要重命名变量名,则必须手动执行。
  • 但是,您的灵活性要高得多,例如
    • 您可以决定覆盖现有变量或…
    • …选择实现常量变量
    • 对不同类型的覆盖引发异常
    • 等。

也就是说,我实现了一个变量管理器类,它提供了上面的一些想法。它适用于python 2和3。

你可以这样使用这个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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'])

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

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


请参阅以下示例以创建变量运行时。您可以使用globals()

1
2
for i in range(3):
    globals() ['variable_'+str(i)] = i

在上面的例子中,我想创建三个变量:运行时的variable_0variable_1variable_2,分别为0、1和2。

1
2
3
4
5
6
7
8
variable_0
[Output]:0

variable_1
[Output]:1

variable_2
[Output]:2

要访问运行时创建的变量值,可以使用eval()方法,如下所示:

1
2
3
4
5
6
7
8
for i in range(3):
globals() ['variable_'+str(i)] = i
print('Variable Value:',eval('variable_'+str(i)))

[Output]:
Variable Value: 0
Variable Value: 1
Variable Value: 2

有一种已知的方法来模拟变量的容器,它支持两种访问方法:通过变量的名称和字符串键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Vars:
    def __init__(self, **kw):
        self.__dict__.update(kw)

    def __getitem__(self, key):
        return self.__dict__[key]

    def __setitem__(self, key, val):
        self.__dict__[key] = val

    def __contains__(self, name):
        return name in self.__dict__

    def __nonzero__(self):
        return bool(self.__dict__)

    def __iter__(self):
        return iter(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

    def __copy__(self):
        return self.__class__(**self.__dict__)

    def __repr__(self):
        return 'Vars(' + ', '.join('%s=%r' % (k,v) for k,v in self.__dict__.items()) + ')'

>>> vars = Vars()
>>> vars.a = 1
>>> vars['b'] = 2
>>> print(vars)
Vars(a=1, b=2)
>>> print(vars['a'], vars.b)
1 2
>>> print(tuple(vars))
('a', 'b')

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

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

1
2
3
4
5
6
7
8
9
10
11
# 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

您可以使用内置函数vars()

1
2
3
4
>>> foo = 'bar'
>>> vars()[foo] = 'something'
>>> bar
'something'