【问题标题】:How do I create variable variables?如何创建可变变量?
【发布时间】:2022-11-23 07:03:55
【问题描述】:

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

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

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


如果你只是想查找现有的变量的名称,请参阅How can I select a variable by (string) name?。但是,首先考虑是否可以按照此问题中的建议重新组织代码以避免这种需要。

【问题讨论】:

  • 导致恐怖的是维护和调试方面。想象一下,当您的代码中没有实际更改“foo”的地方时,您试图找出变量“foo”的更改位置。进一步想象,你要维护的是别人的代码……好吧,你现在可以去你开心的地方了。
  • 到目前为止还没有提到的另一个陷阱是,如果这样一个动态创建的变量与您的逻辑中使用的变量具有相同的名称。你基本上打开你的软件作为给定输入的人质。
  • 您可以通过访问它们的基础字典来修改全局和局部变量;从维护的角度来看,这是一个可怕的想法......但它可以通过全局变量().更新()当地人()。更新()(或者通过保存其中任何一个的字典引用并像使用任何其他字典一样使用它)。不建议......但你应该知道这是可能的。
  • @JimDennis 实际上,不可以。修改locals返回的字典不会影响 Python 中的本地命名空间.这是不这样做的另一个原因。
  • @juanpa.arrivillaga:我曾尝试在 IPython shell 中对此进行测试,但在顶层进行(其中 locals() 的行为类似于 globsls())。在嵌套代码(在函数定义内)中重做该测试确实表明我无法从中修改 locals() 。正如您所说,当地人的帮助(3.7.6)确实警告:“注意:更新本词典是否会影响本地范围内的名称查找,反之亦然取决于实现并且不受任何向后兼容性保证的保护。”

标签: python variable-variables


【解决方案1】:

您可以使用dictionaries 来完成此操作。字典是键和值的存储。

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

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

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

对于有序序列,列表比具有整数键的字典更方便,因为列表支持索引顺序迭代、slicingappend,以及其他需要使用字典进行笨拙的键管理的操作。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • locals().update({'new_local_var':'一些本地值'})在 Python 3.7.6 中对我来说工作得很好;所以当你说你不能通过它赋值时,我不确定你的意思。
      • 给定 x = "foo"locals()["x"] = "bar" 使用 print x 给出 Jython 2.5.2 的输出 bar。这是使用 maximo 中的按需自动化脚本进行测试的。
      • locals() 的文档明确说:“这本字典的内容应该不是被修改。”(强调我的)
      • @JimDennis`locals()`` 提供了一个字典创建代表局部变量。更新它并不能保证更新实际的局部变量。在现代 Python 实现中,它更像是一张漂亮的框架(高级 dict)中的图片(显示内容)——在图片上绘制实际上不会改变真实的东西。
      • 它不起作用的原因,至少在 CPython 上,是 CPython 为本地分配一个固定大小的数组,并且所述数组的大小是在定义函数时确定的,而不是在运行时确定的,并且不能更改(访问真正的本地人甚至不使用名称;名称在函数编译时被替换为数组中的索引)。 locals() 返回真值 dict;在一个函数中,dict 是通过在您调用 locals() 时在数组中加载名称和关联值而创建的,它不会看到未来的更改。如果它发生变化,则您处于全局或类范围(使用 dict 范围)。
      【解决方案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(*n), 在哪里是命名变量的数量和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'])
          vars = Variables(34, 45)
          print(vars.first, vars.second)
          

          【讨论】:

          • 请记住namedtuples 是不可变的,因此它们与简单地使用点符号的命令有点不同。话虽如此,这两个选项都促进了良好的设计原则,并且不会像该线程中的一半答案那样滥用全局名称空间。
          【解决方案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
            

            【讨论】:

            • 但是,这不适用于 __dict__ 变量。我想知道是否有一个通用的机制来创建任何全局变量动态。
            • globals()可以做到这一点
            【解决方案10】:

            您必须使用 globals() built in method 来实现该行为:

            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
            

            【讨论】:

              【解决方案11】:
              # Python 3.8.2 (default, Feb 26 2020, 02:56: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() 将产生相同的结果

              # Python 3.8.2 (default, Feb 26 2020, 02:56:10)
              
              #<?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
              

              【讨论】:

                【解决方案12】:

                我在回答问题:How to get the value of a variable given its name in a string? 已作为副本关闭,并带有指向此问题的链接。

                如果有问题的变量是一个对象的一部分(例如类的一部分),那么一些有用的函数可以准确地实现这些是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")
                

                “初始变量”

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

                “实际上不是初始的”

                【讨论】:

                  【解决方案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变量
                      • 在覆盖不同类型时引发异常

                    也就是说,我已经实现了一个 variable variables manager-class,它提供了上述一些想法。它适用于 python 2 和 3。

                    你会像这样使用the class

                    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

                        【讨论】:

                        • 这在函数内部不起作用。它本质上等同于更安全的locals()['b'] = 5(它在函数中也不起作用)。
                        • @benrg 你知道如何绕过 Rubens 失败的建议吗?因为我遇到了同样的情况?我有一个文件,其中包含一长串变量赋值作为集合字符串。我需要将它们转换为 python 赋值,但 eval() 和 exec() 都失败了。
                        【解决方案17】:

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

                        语法是这样的——

                        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)
                        

                        【讨论】:

                          猜你喜欢
                          相关资源
                          最近更新 更多