【问题标题】:How to change values in a tuple?如何更改元组中的值?
【发布时间】:2012-07-12 18:27:42
【问题描述】:

我有一个名为 values 的元组,其中包含以下内容:

('275', '54000', '0.0', '5000.0', '0.0')

我想更改此元组中的第一个值(即275),但我知道元组是不可变的,因此values[0] = 200 将不起作用。我怎样才能做到这一点?

【问题讨论】:

  • 元组是不可变的,你需要创建一个新的元组来实现这一点。

标签: python tuples


【解决方案1】:

可以通过:

t = ('275', '54000', '0.0', '5000.0', '0.0')
lst = list(t)
lst[0] = '300'
t = tuple(lst)

但是,如果您需要更改某些内容,最好将其保留为 list

【讨论】:

  • 一个用例是,如果您要存储大量的小序列,其中的值很少更改,但在少数情况下他们可能想要更改。对于一个小但非零长度的序列,元组(一个元素为 60 字节)与列表(104 字节)的内存消耗有所不同。另一个用例是命名元组,因为命名列表本身并不存在。
  • “你为什么要这样做”的回答让我在 StackOverflow 上发疯。不要假设原始海报“想要”这样做。实际上,最好不要假设原始海报是从头开始创造这一切的,也不知道更好。更多时候,我们正在处理来自另一个模块或数据源的输出,其格式或类型是我们无法控制的。
  • @rtphokie 想要改变一个不可变的容器(因此“为什么”是一个非常有效的问题)不同于解释不同的格式,其中一些可能是一个元组。感谢您的发泄:)
  • 重铸为列表并使用临时变量似乎没有必要且内存效率低下。你可以解压成一个同名的元组,然后解压更新任何需要更新的东西。
  • @JonClements 您提出了一个非常有价值的观点,即这样做是不好的做法。这应该在你的答案中。然而,修辞问题通常被解释为不必要的贬义。此类信息以以下形式更好地结构化:“这是不好的做法,因为...” 甚至 “如果您真的需要此信息,请仔细考虑;它通常意味着设计中的错误,因为。 ..”
【解决方案2】:

根据您的问题,切片可能是一个非常巧妙的解决方案:

>>> b = (1, 2, 3, 4, 5)
>>> b[:2] + (8,9) + b[3:]
(1, 2, 8, 9, 4, 5)
>>> b[:2] + (8,) + b[3:]
(1, 2, 8, 4, 5)

这允许您添加多个元素或替换一些元素(特别是如果它们是“邻居”。在上述情况下,转换为列表可能更合适和可读(即使切片符号更短) .

【讨论】:

  • 这对我来说是最好的答案。我很乐意看到它默认在 python 中实现,类似于NamedTuple._replace(fieldname=fieldvalue):tuple._replace(fieldindex, fieldvalue)
【解决方案3】:

嗯,正如 Trufa 已经展示的那样,基本上有两种方法可以在给定索引处替换元组的元素。要么将元组转换为列表,替换元素并转换回来,要么通过连接构造一个新的元组。

In [1]: def replace_at_index1(tup, ix, val):
   ...:     lst = list(tup)
   ...:     lst[ix] = val
   ...:     return tuple(lst)
   ...:

In [2]: def replace_at_index2(tup, ix, val):
   ...:     return tup[:ix] + (val,) + tup[ix+1:]
   ...:

那么,哪种方法更好,也就是更快?

事实证明,对于短元组(在 Python 3.3 上),连接实际上更快!

In [3]: d = tuple(range(10))

In [4]: %timeit replace_at_index1(d, 5, 99)
1000000 loops, best of 3: 872 ns per loop

In [5]: %timeit replace_at_index2(d, 5, 99)
1000000 loops, best of 3: 642 ns per loop

然而,如果我们查看更长的元组,列表转换是可行的方法:

In [6]: k = tuple(range(1000))

In [7]: %timeit replace_at_index1(k, 500, 99)
100000 loops, best of 3: 9.08 µs per loop

In [8]: %timeit replace_at_index2(k, 500, 99)
100000 loops, best of 3: 10.1 µs per loop

对于很长的元组,列表转换要好得多!

In [9]: m = tuple(range(1000000))

In [10]: %timeit replace_at_index1(m, 500000, 99)
10 loops, best of 3: 26.6 ms per loop

In [11]: %timeit replace_at_index2(m, 500000, 99)
10 loops, best of 3: 35.9 ms per loop

此外,连接方法的性能取决于我们替换元素的索引。对于列表方法,索引无关紧要。

In [12]: %timeit replace_at_index1(m, 900000, 99)
10 loops, best of 3: 26.6 ms per loop

In [13]: %timeit replace_at_index2(m, 900000, 99)
10 loops, best of 3: 49.2 ms per loop

所以:如果您的元组很短,请切片并连接。 如果很长,请进行列表转换!

【讨论】:

  • @ErikAronesty 并非总是如此。曾经有用的情况是扩展一个您无法更改的类,并且其方法返回一个您只想更改第一个元素的元组。 return (val,) + res[1:] 比 res2=list(res); res2[0] = 值;返回元组(res2)
  • 我很想看看@BrianSpiering 的回答时间。我想它会(很多?)比串联快,并且可能比转换为列表并返回更快。
  • 我做了计时,BrianSpiering 的方法在这里的所有 3 种情况下始终比其他两种慢,尽管与元组连接方法(Linux 上的 Python 3.7.6)没有太大区别
【解决方案4】:

正如 Hunter McMillen 所说,元组是不可变的,您需要创建一个新元组来实现这一点。例如:

>>> tpl = ('275', '54000', '0.0', '5000.0', '0.0')
>>> change_value = 200
>>> tpl = (change_value,) + tpl[1:]
>>> tpl
(200, '54000', '0.0', '5000.0', '0.0')

【讨论】:

    【解决方案5】:

    可以用一个衬里:

    values = ('275', '54000', '0.0', '5000.0', '0.0')
    values = ('300', *values[1:])
    

    【讨论】:

    • 如何只更改values 中的第三个元素?
    • 以下是您可以更改元组中任何元素的方法 - i = 2; values = (*values[:i], '300', *values[i+1:])
    • 这是一个非常优雅的解决方案,但事实证明它比@DaveHalter 的答案中的元组连接方法慢了一点。
    【解决方案6】:

    您不能修改元组中的项目,但可以修改元组中可变对象的属性(例如,如果这些对象是列表或实际的类对象)

    例如

    my_list = [1,2]
    tuple_of_lists = (my_list,'hello')
    print(tuple_of_lists) # ([1, 2], 'hello')
    my_list[0] = 0
    print(tuple_of_lists) # ([0, 2], 'hello')
    

    【讨论】:

    • 有趣。感谢那。元组的元素似乎是对其他数据对象的引用。
    【解决方案7】:

    我相信这在技术上回答了这个问题,但不要在家里这样做。目前,所有答案都涉及创建新元组,但您可以使用ctypes 修改内存中的元组。依靠CPython在64位系统上的各种实现细节,一种方法如下:

    def modify_tuple(t, idx, new_value):
        # `id` happens to give the memory address in CPython; you may
        # want to use `ctypes.addressof` instead.
        element_ptr = (ctypes.c_longlong).from_address(id(t) + (3 + idx)*8)
        element_ptr.value = id(new_value)
        # Manually increment the reference count to `new_value` to pretend that
        # this is not a terrible idea.
        ref_count = (ctypes.c_longlong).from_address(id(new_value))
        ref_count.value += 1
    
    t = (10, 20, 30)
    modify_tuple(t, 1, 50)   # t is now (10, 50, 30)
    modify_tuple(t, -1, 50)  # Will probably crash your Python runtime
    

    【讨论】:

    • 总是很高兴知道一些与通常答案不同的东西。干杯!
    • 想用 C 编写代码的人可能应该这样做。像这样破解解释器只是错过了这里的主题。这也是不可靠的,因为 cPython 实现细节可以随时更改而不会发出警告,并且可能会破坏任何依赖于不可变元组的代码。此外,元组是python中最轻量级的集合对象,所以创建一个新的没有问题。如果您绝对需要经常修改集合,请改用列表。
    • 您忘记将引用计数减少到您要丢弃的值。这会导致泄漏。
    • python 不会为元组保留唯一的 id 吗?这样做,您最终可能会得到两个(不同的)具有相同 id 的元组。根据 CPython 管理这种唯一性的方式,具有这些值的新元组可能会以不同的 id 结束(因此它不会评估为等于修改后的值)。
    【解决方案8】:

    并不是说这很优越,但是如果有人好奇,可以用一行来完成:

    tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])
    

    【讨论】:

    • 比tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple)) 快吗? (为什么不是生成器理解?)
    【解决方案9】:

    首先,问问自己为什么要改变你的tuple。 strings and tuple are immutable in Ptyhon是有原因的,如果你想改变你的tuple,那么它可能应该是list。

    其次,如果您仍然希望改变您的元组,那么您可以将您的 tuple 转换为 list,然后将其转换回来,并将新的元组重新分配给同一个变量。这很好如果你只想改变你的元组一次。否则,我个人认为这是违反直觉的。因为它本质上是在创建一个新的元组,每次如果你想改变元组,你都必须执行转换。此外,如果您阅读了代码,会令人困惑地认为为什么不创建一个list?但它很好,因为它不需要任何库。

    我建议使用mutabletuple 0.2 中的mutabletuple(typename, field_names, default=MtNoDefault)。我个人认为这种方式更直观和可读性。个人阅读代码会知道作者打算在未来改变这个元组。与上面的list 转换方法相比,缺点是这需要您导入额外的py文件。

    from mutabletuple import mutabletuple
    
    myTuple = mutabletuple('myTuple', 'v w x y z')
    p = myTuple('275', '54000', '0.0', '5000.0', '0.0')
    print(p.v) #print 275
    p.v = '200' #mutate myTuple
    print(p.v) #print 200
    

    TL;DR:不要试图改变tuple。如果您这样做并且这是一次性操作,请将tuple 转换为列表,对其进行变异,将list 转换为新的tuple,然后重新分配回保存旧tuple 的变量。如果需要 tuple 并且想避免 list 并且想要多次变异,则创建 mutabletuple。

    【讨论】:

      【解决方案10】:

      编辑:这不适用于具有重复条目的元组!

      基于Pooya's idea:

      如果你打算经常这样做(你不应该这样做,因为元组是不可变的)你应该这样做:

      def modTupByIndex(tup, index, ins):
          return tuple(tup[0:index]) + (ins,) + tuple(tup[index+1:])
      
      print modTupByIndex((1,2,3),2,"a")
      

      或者基于Jon's idea:

      def modTupByIndex(tup, index, ins):
          lst = list(tup)
          lst[index] = ins
          return tuple(lst)
      
      print modTupByIndex((1,2,3),1,"a")
      

      【讨论】:

        【解决方案11】:

        你不能。如果你想改变它,你需要使用列表而不是元组。

        请注意,您可以改为创建一个新元组,将新值作为其第一个元素。

        【讨论】:

          【解决方案12】:

          我这样做了:

          list = [1,2,3,4,5]
          tuple = (list)
          

          要改变,就去做

          list[0]=6
          

          你可以改变一个元组:D

          这里是完全从 IDLE 复制过来的

          >>> list=[1,2,3,4,5,6,7,8,9]
          
          >>> tuple=(list)
          
          >>> print(tuple)
          
          [1, 2, 3, 4, 5, 6, 7, 8, 9]
          
          >>> list[0]=6
          
          >>> print(tuple)
          
          [6, 2, 3, 4, 5, 6, 7, 8, 9]
          

          【讨论】:

          • 元组是一个列表,而不是一个元组。 x = (y) 只是将 y 分配给 x。
          • 同样通过使用名称“tuple”和“list”作为变量,以后比较元组和列表类型会变得很困难。
          【解决方案13】:

          我玩游戏迟到了,但我认为最简单、资源最友好和最快的方式(视情况而定), 是覆盖元组本身。因为这将消除创建列表和变量的需要,并存档在一行中。

          new = 24
          t = (1, 2, 3)
          t = (t[0],t[1],new)
          
          >>> (1, 2, 24)
          

          但是:这仅适用于相当小的元组,并且也将您限制为固定的元组值,但是,无论如何,大多数时候元组都是这种情况。

          所以在这种特殊情况下,它看起来像这样:

          new = '200'
          t = ('275', '54000', '0.0', '5000.0', '0.0')
          t = (new, t[1], t[2], t[3], t[4])
          
          >>> ('200', '54000', '0.0', '5000.0', '0.0')
          

          【讨论】:

          • 只有当这是一个已知长度的静态元组时才有效。不是代码迟早会失败,因为它太具体了......
          • 是的-@patroqueeet,因此我在 But:...- 之后清楚地说明了这种方法的缺点。请重新考虑您的反对意见,谢谢 ;)
          • 嘿,重新考虑并单击按钮。但投票现在被 SO 锁定:/
          【解决方案14】:

          您可以通过引用复制来更改元组的值

          >>> tuple1=[20,30,40]
          
          >>> tuple2=tuple1
          
          >>> tuple2
              [20, 30, 40]
          
          >>> tuple2[1]=10
          
          >>> print(tuple2)
              [20, 10, 40]
          
          >>> print(tuple1)
              [20, 10, 40]
          

          【讨论】:

          • 只是这些是列表,而不是元组,无论是否有第二个引用,您都可以更改它们。
          【解决方案15】:

          基于Jon的想法和亲爱的Trufa

          def modifyTuple(tup, oldval, newval):
              lst=list(tup)
              for i in range(tup.count(oldval)):
                  index = lst.index(oldval)
                  lst[index]=newval
          
              return tuple(lst)
          
          print modTupByIndex((1, 1, 3), 1, "a")
          

          它会改变你所有的旧值出现

          【讨论】:

          • 如果您要更改多个值,这会很不舒服(因为没有更好的词),但话说回来,您为什么要首先修改元组...
          • @Trufa 是的,我正在尝试写它:D
          • 方法名 modify_tuple_by_index 不准确,势必造成混乱。
          【解决方案16】:

          我发现编辑元组的最佳方法是使用以前的版本作为基础重新创建元组。

          这是我用来制作颜色较浅版本的示例(当时我已经打开了它):

          colour = tuple([c+50 for c in colour])
          

          它的作用是遍历元组“颜色”并读取每个项目,对其进行处理,最后将其添加到新元组中。

          所以你想要的是这样的:

          values = ('275', '54000', '0.0', '5000.0', '0.0')
          
          values  = (tuple(for i in values: if i = 0: i = 200 else i = values[i])
          

          那个特定的不起作用,但这个概念是你需要的。

          tuple = (0, 1, 2)
          

          tuple = 遍历元组,根据需要更改每个项目

          就是这个概念。

          【讨论】:

            【解决方案17】:

            tldr; “解决方法”是创建一个新的元组对象,而不是实际修改原始对象

            虽然这是一个非常古老的问题,但有人告诉我这个 Python 变异元组的疯狂。我对此感到非常惊讶/好奇,并进行了一些谷歌搜索,我来到了这里(以及其他类似的在线示例)

            我进行了一些测试来证明我的理论

            注意== 值相等,而is 引用相等(obj a 与 obj b 是否相同)

            a = ("apple", "canana", "cherry")
            b = tuple(["apple", "canana", "cherry"])
            c = a
            
            print("a: " + str(a))
            print("b: " + str(b))
            print("c: " + str(c))
            print("a == b :: %s" % (a==b))
            print("b == c :: %s" % (b==c))
            print("a == c :: %s" % (a==c))
            print("a is b :: %s" % (a is b))
            print("b is c :: %s" % (b is c))
            print("a is c :: %s" % (a is c))
            
            d = list(a)
            d[1] = "kiwi"
            a = tuple(d)
            
            print("a: " + str(a))
            print("b: " + str(b))
            print("c: " + str(c))
            print("a == b :: %s" % (a==b))
            print("b == c :: %s" % (b==c))
            print("a == c :: %s" % (a==c))
            print("a is b :: %s" % (a is b))
            print("b is c :: %s" % (b is c))
            print("a is c :: %s" % (a is c))
            

            产量:

            a: ('apple', 'canana', 'cherry')
            b: ('apple', 'canana', 'cherry')
            c: ('apple', 'canana', 'cherry')
            a == b :: True
            b == c :: True
            a == c :: True
            a is b :: False
            b is c :: False
            a is c :: True
            a: ('apple', 'kiwi', 'cherry')
            b: ('apple', 'canana', 'cherry')
            c: ('apple', 'canana', 'cherry')
            a == b :: False
            b == c :: True
            a == c :: False
            a is b :: False
            b is c :: False
            a is c :: False
            

            【讨论】:

              猜你喜欢
              • 2013-06-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-06-18
              • 1970-01-01
              • 2017-05-02
              • 2015-05-01
              相关资源
              最近更新 更多