Python:更改元组中的值


124

我是python的新手,所以这个问题可能有点基本。我有一个元组values,其中包含以下内容:

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

我想更改275此元组中的第一个值(即),但我知道元组是不可变的,因此values[0] = 200将不起作用。我该如何实现?


24
元组是不可变的,您需要创建一个新的元组以实现此目的。
亨特·麦克米伦

Answers:


178

首先,您需要问,为什么要这样做?

但是可以通过:

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

但是,如果您需要进行更改,最好将其保留为 list


4
一种用例是,如果您存储大量的小序列,这些序列的值很少更改,但是在少数情况下可能会希望更改。对于较小但非零长度的序列,元组(一个元素为60字节)与列表(104字节)的内存消耗有所不同。另一个用例是namedtuple,因为namedlist本身并不存在。
Michael Scott Cuthbert

81
“为什么要这样做”响应使我对StackOverflow感到困惑。不要假设原始海报“想要”那样做。实际上,最好不要以为原始海报是从头开始创建所有这些都不会更好。通常,我们以无法控制的格式或类型处理来自另一个模块或数据源的输出。
rtphokie

9
想要突变一个不可变容器的@rtphokie(因此“为什么”是一个非常有效的问题)与解释不同的格式(其中某些可能是元组)不同。不过感谢您的发泄:)
乔恩·克莱门茨

7
重铸到列表并使用临时变量似乎不必要并且内存效率低下。您可以将其解压缩为具有相同名称的元组,而在解压缩更新时则需要进行更新。
Brian Spiering '18 -10-5

5
@JonClements您提出了一个非常有价值的观点,即这样做是不好的做法。那应该是你的答案。然而,修辞问题常常被解释为不必要的贬义。这样的信息可以用以下形式更好地组织:“这是一个不好的做法,因为...”,或者甚至是“如果真的需要,请仔细考虑;这通常表示设计有缺陷,因为...”
Philip Couling

74

根据您的问题,切片可以是一个非常整洁的解决方案:

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

这使您可以添加多个元素或替换一些元素(尤其是在它们是“邻居”时。在上述情况下,强制转换为列表可能更合适且更易读(即使切片表示法要短得多)。


24

好吧,正如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

因此:如果您的元组很短,则切片并连接。如果很长,请执行列表转换!


1
@ErikAronesty并非总是如此。一旦有用的情况扩展了一个您不能更改的类,并且该类的方法返回一个元组,您只想从中更改第一个元素。return(val,)+ res [1:]比res2 = list(res)更清晰;res2 [0] = val; 返回元组(res2)
yucer


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))吗?(为什么不理解生成器?)
匿名

8

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

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

1
知道与平常答案不同的东西总是很高兴。干杯!
Kartheek Palepu '18

想用C语言进行编码的人可能应该做到这一点。像这样破解解释器只是错过了这里的话题。这也是不可靠的,因为cPython的实现细节可以随时更改而不会发出警告,并且很可能破坏依赖于元组的任何代码。此外,元组是python中最轻量的集合对象,因此创建一个新元组没有问题。如果绝对需要经常修改集合,请使用列表。
Bachsau

1
您忘记将引用计数递减为要丢弃的值。那会导致泄漏。
wizzwizz4

6

正如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')

3

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

基于Pooya的想法

如果您打算经常执行此操作(由于元组由于某种原因是不变的,则不应该这样做),您应该执行以下操作:

def modTupByIndex(tup, index, ins):
    return tuple(tup[0:index]) + (ins,) + tuple(tup[index+1:])

print modTupByIndex((1,2,3),2,"a")

或基于乔恩的想法

def modTupByIndex(tup, index, ins):
    lst = list(tup)
    lst[index] = ins
    return tuple(lst)

print modTupByIndex((1,2,3),1,"a")

3

第一拳,问自己为什么要突变你的tuple在Ptyhon中字符串和元组是不可变的,这是有原因的,如果您想对自己的变量进行突变,tuple那么它应该是一个list替代。

其次,如果您仍然希望对元组进行突变,则可以将转换tuple为,list然后再转换回去,然后将新的元组重新分配给相同的变量。如果您只想对元组进行一次变异,那就太好了。否则,我个人认为这是违反直觉的。因为它本质上是在创建一个新的元组,并且每次您想要对元组进行突变时,都必须执行转换。另外,如果您阅读了代码,那么思考为什么不仅仅创建一个list?但这很好,因为它不需要任何库。

我建议mutabletuple(typename, field_names, default=MtNoDefault)mutabletuple 0.2使用。我个人认为这种方式是一个更直观可读性。阅读该代码的人会知道编写者打算将来更改此元组。与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变量,然后重新分配给持有old的变量tuple。如果欲望tuple和某种原因想避免list并想变异多于一次,那就创造mutabletuple


2

基于乔恩的思想和亲爱的特鲁法。

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 2012年

@Trufa是的,我正在尝试写它:D
Pooya

方法名称modify_tuple_by_index不正确,势必引起混乱。
msw 2012年

1

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

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


0

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

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

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)

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

这就是概念。


0

我来晚了,但是我认为最简单,资源友好和最快的方法(取决于情况)是覆盖元组本身。由于这将消除对列表和变量创建的需要,因此将其归档在一行中。

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

是的-@patroqueeet,因此我在But:...-之后清楚地说明了这种方法的缺点。请重新考虑您的不赞成票,谢谢;)
GordanTrevis 19/12/30

1
嘿,重新考虑并单击按钮。但投票现在被SO锁定:/
patroqueeet

0

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

0

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

例如

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

-2

我这样做:

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]

2
元组是一个列表,而不是一个元组。x = (y)除了将y赋给x以外,什么都不做。
m4tx 2015年

2
同样通过使用名称“ tuple”和“ list”作为变量,您以后将很难比较元组和列表类型。
Michael Scott Cuthbert

-4

您可以使用按引用复制来更改元组的值

>>> tuple1=[20,30,40]

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

>>> print(tuple2)
    [20, 10, 40]

>>> print(tuple1)
    [20, 10, 40]

1
只是这些是列表,而不是元组,无论是否提供第二个引用,您都可以更改它们。
巴查(Bachsau)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.