如何创建可变变量?

2024-11-15 08:36:00
admin
原创
52
摘要:问题描述:我知道一些其他语言,例如 PHP,支持“可变变量名”的概念 - 也就是说,字符串的内容可以作为变量名的一部分。我听说这总体来说是个坏主意,但我认为它可以解决我的 Python 代码中的一些问题。在 Python 中可以做这样的事情吗?会有什么问题吗?如果您只是想通过名称查找现有变量,请参阅如何通过(...

问题描述:

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

我听说这总体来说是个坏主意,但我认为它可以解决我的 Python 代码中的一些问题。

在 Python 中可以做这样的事情吗?会有什么问题吗?


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


解决方案 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以及其他需要使用字典进行尴尬的键管理的操作。

解决方案 2:

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

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

解决方案 3:

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

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

如果您想访问本地范围内的变量,您可以使用locals(),但不能为返回的字典赋值。

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

解决方案 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*是需要访问(包括创建)该组变量的次数。更精明的初学者会注意到,每行中唯一的区别是一个根据规则变化的数字,并决定使用循环。然而,他们不知道如何动态创建这些变量名,可能会尝试这样的方法:

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

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

解决方案 5:

每当你想使用可变变量时,最好使用字典。因此,不要写

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

你写

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

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

解决方案 6:

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

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

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

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

>>> i_5
'a'

解决方案 7:

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)

解决方案 8:

该类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

解决方案 9:

如果您不想使用任何对象,您仍然可以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

解决方案 10:

Python 中的可变变量

"""
<?php
$a = 'hello';
$e = 'wow'
?>
<?php
$$a = 'world';
?>
<?php
echo "$a ${$a}
";
echo "$a ${$a[1]}
";
?>
<?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}
"; ?>
print(a, vars()[vars()['a'][1]]) #<?php echo "$a ${$a[1]}
"; ?>
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}
"; ?>
#<?php echo "$a ${$a[1]}
"; ?>
#<?php echo "$a $hello"; ?>

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

print('

globals():
')
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

解决方案 11:

您必须使用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

解决方案 12:

我正在回答问题如何根据字符串中的名称获取变量的值? ,
该问题已作为重复问题关闭,并附有指向该问题的链接。(编者注:现在已作为重复问题关闭,即如何通过(字符串)名称选择变量?)


如果所讨论的变量是对象的一部分(例如,类的一部分),那么可以实现这一目标的一些有用函数是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'

解决方案 13:

我在 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:

大家一致同意使用字典来解决这个问题 - 请参阅其他答案。在大多数情况下,这是一个好主意,但是,由此引发了许多问题:

  • 你自己将负责这本字典,包括垃圾收集(字典中的变量)等。

  • 变量既没有局部性,也没有全局性,这取决于字典的全局性

  • 如果你想重命名变量名,你必须手动进行

  • 然而,你可以更加灵活,例如

+ 您可以决定覆盖现有变量或......
+ ...选择实现 const 变量
+ 针对不同类型的覆盖引发异常
+ ETC。

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

解决方案 15:

任何变量集都可以包装在类中。可以通过 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

解决方案 16:

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

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

结果:10

解决方案 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)

解决方案 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'?

然后我了解到 format 函数不能按照我想要的方式工作。您不能将任意代码传递给它。您需要将格式化内容传递给它。所以我尝试了另一种解决方案。我编写了代码populate以使用eval和 f 字符串而不是format。f 字符串(与 format 不同)允许在花括号插值中使用 python 代码。因此,像 `f"Hello, {name.split()[0]}" 这样的 f 字符串确实有效。让我们只看这一小部分的代码(这样您就不必离开这篇文章来弄清楚 f 字符串):

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 字符串能够用范围内的任何全局或局部变量填充字符串。为了解决我的问题,我可以将模板更改为, "Hello, {variables['name']}"因为 variables肯定在范围内。这是一种非常糟糕的方法,因为现在模板编写者必须知道字典variables。相反,我想让variables模板作者能够使用字典中的每个键,就像我之前使用的那样format(**variables)

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

我尝试过这个:

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() 字典中设置键值对,在函数中创建局部变量(或全局变量)。

对于eval,第二和第三个参数允许您传递局部变量和全局变量,因此您可以将填充函数简化为这样:

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

以上是我过去eval进行 f 字符串填充(或简单模板评估)的方法。但eval也可用于提供可变变量。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   681  
  在项目管理领域,集成产品开发(IPD)流程以其高效、协同的特点,被众多企业视为提升产品竞争力的关键。IPD流程强调跨部门、跨职能的紧密合作,以确保产品从概念到市场各个环节的无缝衔接。然而,实现这一目标并非易事,它需要企业深刻理解并掌握IPD流程中的跨部门协作艺术。本文将深入探讨IPD流程中跨部门协作的三个关键点,旨在为...
IPD项目管理咨询   9  
  掌握IPD流程图:提升团队协作的关键路径在当今快速变化的商业环境中,团队协作的效率与效果直接关系到项目的成功与否。集成产品开发(Integrated Product Development,简称IPD)作为一种先进的研发管理理念,通过跨部门、跨领域的协同工作,能够显著提升产品开发的速度与质量。而IPD流程图,则是这一理...
IPD流程阶段   9  
  IPD流程概述:理解其核心价值与实施背景集成产品开发(Integrated Product Development,简称IPD)是一种先进的产品开发管理理念,它强调跨部门协作、市场导向和快速响应变化的能力。IPD流程不仅关注产品本身的技术创新,更注重将市场、研发、生产、销售等各个环节紧密集成,以实现产品从概念到市场的高...
华为IPD是什么   7  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程以其跨部门协作、高效决策和快速响应市场变化的特点,被众多企业视为提升竞争力的关键。然而,实践IPD流程并非易事,项目管理中的种种错误往往阻碍了其效果的充分发挥。本文旨在深入探讨如何在实施IPD流程时避免这些常见错误,...
IPD框架   7  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用