如何动态创建变量?[复制]

How can you dynamically create variables? [duplicate]

提问人:Noah R 提问时间:2/18/2011 最后编辑:mkrieger1Noah R 更新时间:5/7/2022 访问量:564311

问:

我想在 Python 中动态创建变量。有没有人有任何创造性的方法来做到这一点?

Python 变量

评论

2赞 wheaties 2/18/2011
你能解释一下“via to a while loop”吗?我不知道这意味着什么。
2赞 Mike 2/18/2011
解决什么问题?需要一些背景信息。
0赞 Noah R 2/18/2011
我想创建一个while循环,并在该循环中让它一遍又一遍地创建变量,直到我告诉它停止。这有什么意义吗?
2赞 Gordon Gustafson 2/18/2011
“动态创建变量”是什么意思?动态如何?你能只使用字典并为其分配各种值吗?
10赞 Gintautas Miliauskas 2/18/2011
@Trivikram:不正确,变量作用域是整个函数,而不仅仅是 while 循环。

答:

208赞 JoshAdel 2/18/2011 #1

除非非常需要创建一堆变量名称,否则我只会使用字典,您可以在其中动态创建键名称并为每个键名称关联一个值。

a = {}
k = 0
while k < 10:
    # dynamically create key
    key = ...
    # calculate value
    value = ...
    a[key] = value 
    k += 1

collections 模块中也有一些有趣的数据结构可能适用。

评论

14赞 eyquem 2/18/2011
@Josh和其他人......在字典中创建项目(键,值)与在字典中创建相同的项目或作为字典实现的项目没有太大区别。我想知道为什么我的答案被一些反对的狙击手投了反对票,而 Gintautas Miliauskas 的答案被投了赞成票,而这个没有给出操作方法的答案被投了 3 次赞成票。alocals()globals()globals()<dynamically create key>
2赞 JoshAdel 2/18/2011
@eyquem我没有给出任何创建密钥的具体方法,因为我对 OP 在实践中需要的命名方案一无所知。相反,我只是给出了最通用和最干净的方案来解决这个问题。
3赞 Duncan 2/18/2011
@eyquem局部变量不是作为字典实现的。几乎总是,该函数从局部变量创建一个字典,但除非您调用 .locals()locals()
3赞 Duncan 2/18/2011
@eyquem,本地命名空间是作为堆栈上的槽实现的,因此字节码可以直接将它们作为堆栈帧中的偏移量引用(加上调用时也包含的自由变量。在 Python 2.x 中,当您在函数内部使用而不为 exec 指定本地命名空间时,情况会有所不同。我不确定这是在哪里记录的:它不是语言定义的一部分,因为不同的实现可以以不同的方式选择如何实现局部变量命名空间。locals()exec
2赞 Marcin 8/16/2014
这是最好的答案。因此,这应该是规范的问题。
118赞 eyquem 2/18/2011 #2

globals() 返回模块变量的字典。您可以通过在该字典上创建一个键来创建新变量:

# By default, a module has some hidden variables defined
print({k: v for k, v in globals().items() if not k.startswith("__")})

for i in range(1, 11):
    globals()[f"my_variable_{i}"] = i

print()
print(my_variable_1)
print(my_variable_2)
# and so on

print()
print({k: v for k, v in globals().items() if not k.startswith("__")})

结果:

{}

1
2

{'i': 10, 'my_variable_1': 1, 'my_variable_2': 2, 'my_variable_3': 3, 'my_variable_4': 4, 'my_variable_5': 5, 'my_variable_6': 6, 'my_variable_7': 7, 'my_variable_8': 8, 'my_variable_9': 9, 'my_variable_10': 10}

评论

4赞 Ned Batchelder 2/18/2011
OP 需要一种方法来创建与许多名称关联的许多值。他将其表述为“动态创建变量”,但使用字典是解决他真正问题的正确方法。
50赞 eyquem 2/18/2011
@Ned 巴切尔德:也许这个问题被误导了。但我认为他最好得到两个答案:1-是的,你可以做你想做的事2-但你想进入的方式并不是一个上帝,因为这个原因。这样做,OP 将学到两件事:Python 功能强大,以及正确编码的一些建议。相反,如果有人被阻止自己思考和思考问题,那就是对brain_storming和反思的束缚
14赞 apiguy 2/18/2011
我认为您被否决的原因是,在全局字典中发送垃圾邮件通常不被认为是最佳实践。事实上,在你自己的代码示例中,如果你的随机密钥生成器要生成一个已经在全局字典中的密钥(例如,密钥“字母表”),你会假设会发生什么。此外,我认为 OP 问题暗示了他需要在创建“变量”后获取它们。他如何使用您的解决方案将他的动态变量与全局范围内已经存在的变量区分开来?在这种情况下,单独的字典更好。
28赞 acjay 3/6/2013
这是一个很好的答案。我偶然发现了答案,并有合理的需要这样做,使我不得不写一个几乎重复的问题。
6赞 Davide R. 6/29/2015
值得指出的是,这个答案提供了动态创建全局变量的方法。这意味着定义的每个变量都将进入模块命名空间,并一直保留在那里,直到程序结束。大多数时候,当人们说“变量”时,它们指的是函数局部变量,它只存在于函数执行的上下文中。在这种情况下,可能不是问题的解决方案。考虑明确使用字典,如JoshAdel提供的答案。globals()globals()
40赞 John Machin 2/18/2011 #3

将内容填充到全局和/或本地命名空间中不是一个好主意。使用字典是如此的另一种语言...... 只是看起来很尴尬。Python 是 OO。用一位大师的话来说:“”命名空间是一个响亮的好主意——让我们做更多这样的想法!“””d['constant-key'] = value

喜欢这个:

>>> class Record(object):
...     pass
...
>>> r = Record()
>>> r.foo = 'oof'
>>> setattr(r, 'bar', 'rab')
>>> r.foo
'oof'
>>> r.bar
'rab'
>>> names = 'id description price'.split()
>>> values = [666, 'duct tape', 3.45]
>>> s = Record()
>>> for name, value in zip(names, values):
...     setattr(s, name, value)
...
>>> s.__dict__ # If you are suffering from dict withdrawal symptoms
{'price': 3.45, 'id': 666, 'description': 'duct tape'}
>>>

评论

2赞 eyquem 2/18/2011
我不明白你想说什么。为什么全局和本地命名空间不是好主意?因为“大多数命名空间目前都是作为 Python 字典实现的”?词典是“某种其他语言”?为什么对词典有这种批评?你认为实例的命名空间比字典更好用吗?井。。。你知道吗:
4赞 eyquem 2/18/2011
“命名空间是从名称到对象的映射。大多数命名空间目前都是作为 Python 字典实现的,但这通常不会以任何方式引起注意(性能除外),并且将来可能会发生变化。命名空间的示例包括:内置名称集(abs() 等函数和内置异常名称);模块中的全局名称;以及函数调用中的本地名称。从某种意义上说,对象的属性集也构成了命名空间。"
0赞 eyquem 2/18/2011
(docs.python.org/release/2.5/tut/...)作者是 GvR 本人,它是用 2.5 版本编写的,但不是更晚的,这就是我给出这个链接的原因
0赞 eyquem 2/18/2011
在字典之外似乎没有救赎。无论如何,Python 是基于对象的,并且在任何地方都使用名称和对象之间的映射,它是数据模型,所以不可能逃避这个事实。我就是这么想的。我错了吗?
0赞 eyquem 2/18/2011
顺便说一句:尽管我有论证,但我更喜欢你的解决方案。它比使用 globals() 更清晰、更简单。我喜欢使用 setattr()
-1赞 eyquem 2/18/2011 #4

对于 free-dom:

import random

alphabet = tuple('abcdefghijklmnopqrstuvwxyz')

globkeys = globals().keys()
globkeys.append('globkeys') # because name 'globkeys' is now also in globals()

print 'globkeys==',globkeys
print
print "globals().keys()==",globals().keys()

for i in xrange(8):
    globals()[''.join(random.sample(alphabet,random.randint(3,26)))] = random.choice(alphabet)
del i

newnames = [ x for x in globals().keys() if x not in globkeys ]
print
print 'newnames==',newnames

print
print "globals().keys()==",globals().keys()

print
print '\n'.join(repr((u,globals()[u])) for u in newnames)

结果

globkeys== ['__builtins__', 'alphabet', 'random', '__package__', '__name__', '__doc__', 'globkeys']

globals().keys()== ['__builtins__', 'alphabet', 'random', '__package__', '__name__', 'globkeys', '__doc__']

newnames== ['fztkebyrdwcigsmulnoaph', 'umkfcvztleoij', 'kbutmzfgpcdqanrivwsxly', 'lxzmaysuornvdpjqfetbchgik', 'wznptbyermclfdghqxjvki', 'lwg', 'vsolxgkz', 'yobtlkqh']

globals().keys()== ['fztkebyrdwcigsmulnoaph', 'umkfcvztleoij', 'newnames', 'kbutmzfgpcdqanrivwsxly', '__builtins__', 'alphabet', 'random', 'lxzmaysuornvdpjqfetbchgik', '__package__', 'wznptbyermclfdghqxjvki', 'lwg', 'x', 'vsolxgkz', '__name__', 'globkeys', '__doc__', 'yobtlkqh']

('fztkebyrdwcigsmulnoaph', 't')
('umkfcvztleoij', 'p')
('kbutmzfgpcdqanrivwsxly', 'a')
('lxzmaysuornvdpjqfetbchgik', 'n')
('wznptbyermclfdghqxjvki', 't')
('lwg', 'j')
('vsolxgkz', 'w')
('yobtlkqh', 'c')

另一种方式:

import random

pool_of_names = []
for i in xrange(1000):
    v = 'LXM'+str(random.randrange(10,100000))
    if v not in globals():
        pool_of_names.append(v)

alphabet = 'abcdefghijklmnopqrstuvwxyz' 

print 'globals().keys()==',globals().keys()

print
for j in xrange(8):
    globals()[pool_of_names[j]] = random.choice(alphabet)
newnames = pool_of_names[0:j+1]

print
print 'globals().keys()==',globals().keys()

print
print '\n'.join(repr((u,globals()[u])) for u in newnames)

结果:

globals().keys()== ['__builtins__', 'alphabet', 'random', '__package__', 'i', 'v', '__name__', '__doc__', 'pool_of_names']


globals().keys()== ['LXM7646', 'random', 'newnames', 'LXM95826', 'pool_of_names', 'LXM66380', 'alphabet', 'LXM84070', '__package__', 'LXM8644', '__doc__', 'LXM33579', '__builtins__', '__name__', 'LXM58418', 'i', 'j', 'LXM24703', 'v']

('LXM66380', 'v')
('LXM7646', 'a')
('LXM8644', 'm')
('LXM24703', 'r')
('LXM58418', 'g')
('LXM84070', 'c')
('LXM95826', 'e')
('LXM33579', 'j')

评论

1赞 Ned Batchelder 2/18/2011
-1:说两遍并不能使它成为一个好主意。修改 globals() 是不好的,只能在非常特殊的情况下进行,通常涉及编码工具。如果您需要一组动态的命名值,请使用字典,这就是它们的用途。
1赞 Ned Batchelder 2/18/2011
为什么要添加到 globals() 只是为了必须转身并保留一个单独的列表,其中包含您转储在那里的所有名称?字典将以更干净、更简单、更易于理解、更灵活、更安全的方式为您处理所有这些。仅仅因为你可以做某事并不意味着你应该做某事。
1赞 eyquem 2/19/2011
@Ned Batchelder 两次投反对票并没有让我更加了解。我不假装这两个代码是完美的论据:它们只是对 free-dom 在两点上的批评的回答。他们不想证明用它来创建一个名称为 X 的新对象是好的,该对象将按如下方式使用:等等等等;globals()['X']li.append(X)
2赞 eyquem 2/19/2011
@Ned Batchelder 现在,多亏了 John Macin 的回答和代码,我知道了另一种方式:使用 setattr() 在名为 X 的实例中创建一个新属性:这很好。或者我什么都不懂。到现在为止,我写了几段代码,John Machin 也写了,Duncan 也给了我精确的解释,而你只是满足于两次投反对票,并发出教条式的句子,说修改全局变量 () 是不好的 你的反对票开始让我感到厌烦,他们不让我理解
3赞 eyquem 2/19/2011
@Ned Batchelder 此外,修改 globals() 绝对不会是坏事,因为它是在我们刚刚编写 or 时修改的。我认为这在很大程度上是因为写作看起来又长又奇怪,以至于它被考虑得很糟糕。但真正的怪异在哪里?我不明白。v = 48for i in xrange(20)globals()["v"] = 48
84赞 chris-piekarski 9/15/2011 #5

使用 exec() 方法运行任意代码。例如,假设您有一个字典,并且想要将每个键转换为具有其原始字典值的变量,则可以执行以下操作:

>>> c = {"one": 1, "two": 2}
>>> for k, v in c.items():
...     exec(f"{k} = {v}")
... 
>>> one
1
>>> two
2

评论

1赞 Tony Suffolk 66 7/3/2014
是的,这会起作用,但我不知道你为什么要这样做——记住显式比隐式好,而且“可读性很重要”。恕我直言,这只是一个坏主意。
8赞 DWilches 1/9/2015
@chris-piekarski 感谢您的回答。想要做这样的事情的一个原因是,当你需要与第三方软件进行通信时,该软件需要将输入作为局部变量传递,而在编译时,你不知道它所期望的变量(例如,我正在创建一个Paraview插件,它施加了这种约束)。
4赞 Davide R. 6/29/2015
请注意,这在函数执行上下文中的 Python 3 中不起作用。在顶层(模块)上下文中它仍然很好。如果您需要创建一个模块变量,我建议编辑字典,或调用模块对象。globals()setattr()
2赞 Davide R. 6/29/2015
我还要强调的是,一般来说,当键或值由外部数据(用户输入、文件或任何东西)提供时,这会带来安全风险。使用 eval/exec 时的常见警告适用。您不希望有人将该值设置为并在您的计算机上执行它。"send_me_all_your_private_data()"
4赞 Douglas 11/17/2016
实际上,这是一个非常好的答案,也是特定情况下的唯一解决方案。假设您有一个表示清单的数组。假设此库存内部可以包含容器,并且这些容器具有自己的库存,表示容器所持有的物品。这是我所知道的唯一方法,能够将 INDEXES 存储在字符串中,并能够更改它,并动态访问不同的索引,例如,如果您更改了要查看的容器。
11赞 Pjl 6/4/2013 #6
vars()['meta_anio_2012'] = 'translate'

评论

8赞 DSM 6/4/2013
这与执行 相同,这在函数中不起作用,并在文档中特别警告。它还受到其他答案的评论中指出的许多问题的影响。locals()['meta_anio_2012'] = 'translate'
3赞 OozeMeister 2/25/2019
使用 vars() 函数修改对象的变量绝对没有错,但您需要稍微更新它以使用它来将变量设置为模块(通过传入模块对象)。 这样做没有错,因为它会获取基础对象的 .只有当被提取的对象对属性具有写入权限时,这才可能不起作用。vars(sys.modules[__name__])['my_variable'] = 'value'__dict__
8赞 Kinsa 4/23/2015 #7

关键字参数允许您将变量从一个函数传递到另一个函数。这样,您可以使用字典的键作为变量名称(可以在循环中填充)。字典名称只需要在调用时前面加上它。while**

# create a dictionary
>>> kwargs = {}
# add a key of name and assign it a value, later we'll use this key as a variable
>>> kwargs['name'] = 'python'

# an example function to use the variable
>>> def print_name(name):
...   print name

# call the example function
>>> print_name(**kwargs)
python

没有,只是一本字典:**kwargs

>>> print_name(kwargs)
{'name': 'python'}
4赞 Reorx 4/27/2015 #8

注意:这应该被视为讨论而不是实际答案。

一种近似的方法是在要创建变量的模块中进行操作。例如,有一个:__main__b.py

#!/usr/bin/env python
# coding: utf-8


def set_vars():
    import __main__
    print '__main__', __main__
    __main__.B = 1

try:
    print B
except NameError as e:
    print e

set_vars()

print 'B: %s' % B

运行它将输出

$ python b.py
name 'B' is not defined
__main__ <module '__main__' from 'b.py'>
B: 1

但是这种方法只适用于单个模块脚本,因为它的导入将始终表示 python 正在执行的入口脚本的模块,这意味着如果涉及其他代码,则变量将在入口脚本的范围内创建,而不是在本身中创建。假设有一个脚本:__main__b.pyBb.pya.py

#!/usr/bin/env python
# coding: utf-8

try:
    import b
except NameError as e:
    print e

print 'in a.py: B', B

运行它将输出

$ python a.py
name 'B' is not defined
__main__ <module '__main__' from 'a.py'>
name 'B' is not defined
in a.py: B 1

请注意,将更改为 .__main__'a.py'

评论

0赞 Shirjeel Ahmed Khan 4/26/2019
我的方法是..,new_var = eval('old_var' + str(count))