如何创建变量?

How do I create variable variables?

提问人: 提问时间:9/3/2009 最后编辑:Community 更新时间:9/26/2023 访问量:256422

问:

我知道其他一些语言,如PHP,支持“变量变量名”的概念 - 也就是说,字符串的内容可以用作变量名称的一部分。

我听说这通常是一个坏主意,但我认为它可以解决我在 Python 代码中遇到的一些问题。

是否可以在 Python 中做这样的事情?会出什么问题?


如果您只是尝试按名称查找现有变量,请参阅如何按(字符串)名称选择变量?。但是,请首先考虑是否可以按照此问题中的建议重新组织代码以避免这种需求。

python 变量

评论

84赞 glenn jackman 9/3/2009
正是维护和调试方面导致了恐怖。想象一下,当你的代码中没有实际更改“foo”的地方时,试图找出变量“foo”在哪里发生了变化。进一步想象一下,你必须维护的是别人的代码......好了,你现在可以去你快乐的地方了。
18赞 holdenweb 12/19/2014
到目前为止,还没有提到的另一个陷阱是,如果这种动态创建的变量与逻辑中使用的变量同名。你基本上把你的软件作为它所给出的输入的人质来打开。
6赞 Jim Dennis 3/19/2020
您可以通过访问全局变量和局部变量的基础字典来修改它们;从维护的角度来看,这是一个可怕的想法......但它可以通过 globals().update() 和 locals().update() 来完成(或者通过保存其中任何一个的字典引用并像任何其他字典一样使用它)。不推荐...但你应该知道这是可能的。
3赞 juanpa.arrivillaga 5/19/2020
@JimDennis实际上,不,它不能。对返回的字典的修改不会影响 CPython 中的本地命名空间。这是不这样做的另一个原因。locals
2赞 Jim Dennis 5/22/2020
@juanpa.arrivillaga:我曾尝试在 IPython shell 中测试过这一点,但在顶层进行了测试(其中 locals() 的行为类似于 globsls())。在嵌套代码中(在函数的定义中)重做该测试确实表明我无法从其中修改 locals()。正如你所说,对本地人员的帮助 (3.7.6) 确实警告说:“注意:对此字典的更新是否会影响本地范围内的名称查找,反之亦然取决于实现,并且不受任何向后兼容性保证的约束。

答:

438赞 c_harm 9/3/2009 #1

您可以使用字典来实现此目的。字典是键和值的存储。

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'x': 1, 'y': 2, '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

49赞 sepp2k 9/3/2009 #2

每当你想使用变量变量时,最好使用字典。所以与其写

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

你写

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

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

117赞 SilentGhost 9/3/2009 #3

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

obj.spam = 'eggs'
name = 'spam'
getattr(obj, name)  # returns 'eggs'
95赞 Nadia Alramli 9/3/2009 #4

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

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

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

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

评论

3赞 Jim Dennis 3/19/2020
locals().update({'new_local_var':'一些本地值'}) 在 Python 3.7.6 中对我来说很好用;所以我不确定你说你不能通过它分配值是什么意思。
0赞 Preacher 3/31/2020
给定和使用给出了 Jython 2.5.2 的输出。这是在 maximo 中使用按需自动化脚本进行测试的。x = "foo"locals()["x"] = "bar"print xbar
8赞 martineau 6/24/2021
locals() 的文档特别指出:“本字典的内容不应被修改。(强调我的)
0赞 MisterMiyagi 11/10/2021
@JimDennis'locals()'' 提供了一个用于表示局部变量的字典。更新它并不能保证更新实际的局部变量。在现代 Python 实现中,它更像是一个漂亮的框架(高级)中的图片(显示内容)——在图片上绘图实际上不会改变真实的东西。dict
3赞 ShadowRanger 12/3/2021
至少在 CPython 上,它不起作用的原因是 CPython 为局部变量分配一个固定大小的数组,并且该数组的大小是在定义函数时确定的,而不是在函数运行时确定的,并且不能更改(对真正的局部变量的访问甚至不使用该名称;该名称在函数编译时替换为数组中的索引)。 返回 true ;在函数中,这是通过在调用时在数组中加载名称和关联值来实现的,它不会看到未来的更改。如果它发生更改,则表示你处于全局或类范围(使用范围)。locals()dictdictlocals()dict
4赞 DomTomCat 6/9/2016 #5

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

  • 你自己将负责这个字典,包括垃圾回收(字典变量)等。
  • 变量变量没有局部性或全局性,这取决于字典的全局性
  • 如果要重命名变量名称,则必须手动执行此操作
  • 但是,您更加灵活,例如
    • 您可以决定覆盖现有变量或...
    • ...选择实现 const 变量
    • 在覆盖不同类型的异常时引发异常
    • 等。

也就是说,我已经实现了一个变量管理器类,它提供了上述一些想法。它适用于 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)
13赞 Andriy Ivaneyko 6/9/2016 #6

您必须使用 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
19赞 ojas mohril 6/22/2016 #7

除了字典,您还可以使用 collections 模块中的 namedtuple,这样可以更轻松地访问。

例如:

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

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

评论

4赞 ggorlen 10/17/2020
请记住,s 是不可变的,因此它们与简单的带有点符号的字典略有不同。话虽如此,这两个选项都促进了良好的设计原则,并且不会像本线程中一半的答案那样滥用全局命名空间。namedtuple
83赞 TigerhawkT3 8/16/2016 #8

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

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)

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

如果程序需要任意变量“names”,则字典是最佳选择,如其他答案中所述。但是,如果您只是尝试创建许多变量,并且不介意用整数序列来引用它们,那么您可能正在寻找一个 .如果您的数据是同质的,例如每日温度读数、每周测验分数或图形小部件网格,则尤其如此。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)]

无论哪种情况,结果都是一个填充的 ,其中第一个元素被 访问,下一个元素被 访问,依此类推。“base”变量名称成为 的名称,并使用不同的标识符来访问它。listmy_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 表示类似对象的序列,a 表示任意排序的对象包,或 a 表示具有关联值的名称包。listsetdict

9赞 patapouf_ai 11/2/2016 #9

我正在回答的问题:如何在字符串中获取给定其名称的变量的值?,该变量作为副本关闭,并带有指向此问题的链接。(编者注:它现在作为 如何通过(字符串)名称选择变量)


如果所讨论的变量是对象的一部分(例如类的一部分),那么一些有用的函数可以准确地实现这些函数是 、 和 。hasattrgetattrsetattr

例如,您可以拥有:

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")
'initial_variable'
>>> v.create_new_var(v.foo, "is actually not initial")
>>> v.initial_variable
'is actually not initial'

评论

0赞 wjandrea 2/9/2023
这将引发一个字符串,这是不允许的。.但是,当你可以无条件地做并让它为你提出时,为什么要烦恼呢?TypeError: exceptions must derive from BaseExceptionhasattr()raisegetattr()AttributeError
2赞 ru13r 8/12/2017 #10

任何一组变量也可以包装在一个类中。 “变量”变量可以在运行时通过__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
18赞 Bill Oldroyd 9/18/2017 #11

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

from types import SimpleNamespace

variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
15赞 Guillaume Lebreton 10/24/2017 #12

如果你不想使用任何对象,你仍然可以在当前模块中使用: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

评论

0赞 Alexey 1/31/2018
但是,这不适用于变量。我想知道是否有一种通用机制可以动态创建任何全局变量。__dict__
2赞 Guillaume Lebreton 1/31/2018
globals()可以这样做
33赞 Rocky Li 11/27/2018 #13

使用(免责声明:这是一种不好的做法,但这是对您的问题最直接的回答,请使用其他数据结构,如已接受的答案)。globals()

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

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

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

>>> i_5
'a'
7赞 Hzzkygcs 12/14/2019 #14

我已经在 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

14赞 mOmOney 1/12/2021 #15

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"; ?>

输出:

hello world
hello wow
hello world

使用 globals()、locals() 或 vars() 将产生相同的结果

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

输出:

locals():

hello world
hello wow
hello world


globals():

hello world
hello wow
hello world

奖励(从字符串创建变量)

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

创建变量并解压缩元组:

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

输出:

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

评论

1赞 wjandrea 2/9/2023
不能在函数内部修改 和 字典。可变的全局状态很糟糕,除非在最简单的脚本中。所以这只有有限的用处。varslocals
2赞 Ruben Medrano 1/30/2021 #16

这应该是极其危险的...... 但是你可以使用 exec():

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

结果: 10

评论

1赞 benrg 10/2/2021
这在函数中不起作用。它本质上等同于更安全的(在函数中也不起作用)。locals()['b'] = 5
0赞 BJonas88 4/17/2022
@benrg 你知道如何绕过鲁本斯失败的建议,因为我陷入了同样的情况吗?我有一个文件,其中包含一长串变量赋值作为集合字符串。我需要将它们转换为 python 赋值,但 eval() 和 exec() 都失败了。
0赞 kannappan 4/2/2022 #17

setattr() 方法设置指定对象的指定属性的值。

语法是这样的——

setattr(object, name, value)
Example –

setattr(self,id,123)

这相当于self.id = 123

正如您可能已经观察到的,setattr() 期望将对象与值一起传递以生成/修改新属性。

我们可以将 setattr() 与解决方法一起使用,以便能够在模块内使用。这是如何——

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

评论

0赞 wjandrea 2/9/2023
它更容易使用。globals()
-1赞 Mark 9/26/2023 #18

TL;DR:考虑使用eval()

我找到这个页面是因为我想用 python 字符串函数做一些简单的模板处理(不包括一个完整的模板引擎)。我一直在寻找如何设置局部变量,这就是我来到这里的原因。

我的问题始于一个简单的模板:

Hello, {name}

我填充此模板的方式是这样的:

def populate(template,variables):
    return template.format(**variables)

所以这将起作用:

values = {
  'name' : 'Stack Overflow' 
}
my_template = "Hello, {name}"
print( populate( my_template , values ) )

# output
Hello, Stack Overflow

但后来事情迅速向南发展。我尝试了一个新模板,我只想要第一个单词:

现在我的模板是这样的:这个代码有一个错误。"Hello, {name.split()[0]}"

values['name'] = "Stack Overflow"
my_template = "Hello, {name.split()[0]}"
print( populate( my_template , values ) 
# output (well, stderr)
AttributeError: 'str' object has no attribute 'split()'. Did you mean: 'split'?

然后我了解到格式函数无法按照我想要的方式工作。您不能向它传递任意代码。你需要通过格式化的东西。所以我尝试了一种不同的解决方案。我编码为使用 和 f 字符串而不是 .f-string(与格式不同)允许在大括号插值中使用 python 代码。因此,像这样的 f 字符串 'f“Hello, {name.split()[0]}” 确实有效。让我们看看这一小部分的代码(所以你不必离开这篇文章来弄清楚f-string):populateevalformat

name = "Stack Overflow"
print(f"Hello, {name.split()[0]}")
# Output:
Hello, Stack

现在我只需要使用 f 字符串。所以我用了.我的新填充是这样的:eval

def populate(template,variables):
    return eval(f'f"{template}"')

但是当我再次运行该程序时,我收到了此错误:

NameError: name 'name' is not defined

我应该指出,f-string 能够用作用域中的任何全局或局部变量填充字符串。为了解决我的问题,我可以将我的模板更改为,因为肯定在范围内。这是非常糟糕的方法,因为现在模板编写者必须了解字典。相反,我想使字典中可用的每个键都可供模板作者使用,就像我之前使用 ."Hello, {variables['name']}"variablesvariablesvariablesformat(**variables)

为了解决我的问题,我想根据传递给函数的字典内容设置局部变量。variablespopulate()

我试过这个:

locals() = variables

但那没有用:

    locals() = variables
    ^^^^^^^^
SyntaxError: cannot assign to function call here. Maybe you meant '==' instead of '='?

然后我尝试了这个,这奏效了:

def populate(template,variables):
    for k,v in variables.items():
      locals()[k] = v
    return eval(f'f"{template}"')


values = {
  'name' : 'Stack Overflow'
}
my_template = "Hello, {name.split()[0]}"
print( populate( my_template , values ) )

因此,第一个要点是,您可以通过在 locals() 字典中设置键值对,在函数(或全局变量)中创建局部变量。

在 的情况下,第二个和第三个参数允许您传入局部和全局变量,因此您可以将 populate 函数简化为:eval

def populate(template,variables):
    return eval(f'f"{template}"',variables)

以上是我过去做f-string填充(或简单模板评估)的方法。但也可用于提供变量。evaleval