Python:改变元组中的值

Daw*_*ood 101 python tuples

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

('275', '54000', '0.0', '5000.0', '0.0')
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*nts 145

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

但它可以通过:

t = ('275', '54000', '0.0', '5000.0', '0.0')
lst = list(t)
lst[0] = '300'
t = tuple(lst)
Run Code Online (Sandbox Code Playgroud)

但是如果你需要改变一些东西,你可能最好还是把它当成一个 list

  • "你为什么要这样做"的反应让我在StackOverflow上疯狂.不要假设原始海报"想要"这样做.实际上,最好不要假设原始海报从头开始创造所有这些并不知道更好.更常见的是,我们正在以我们无法控制的格式或类型处理来自另一个模块或数据源的输出. (54认同)
  • 重新进入列表并使用临时变量似乎没有必要和内存效率低下.您可以解压缩到具有相同名称的元组,并在解压缩时更新任何需要更新的内容. (7认同)
  • 想要改变不可变容器的@rtphokie(因此"为什么"是一个非常有效的问题)与解释不同的格式不同,其中一些格式可能是一个元组.谢谢你发泄:) (6认同)
  • 一个用例是,如果要存储大量的小序列,其中值很少会发生变化,但很少会出现这种情况.对于一个很小但非零长度的序列,元组的内存消耗(单元素为60字节)vs list(104字节)并产生差异.另一个用例是针对namedtuples,因为namedlist本身不存在. (4认同)
  • @JonClements您提出了非常有价值的观点,即这样做是不好的做法。那个*应该*在您的答案中。然而,修辞问题通常被解释为不必要的贬义。这样的信息可以用以下形式更好地组织:*“这是不正确的做法,因为...” *甚至*“如果真的需要,请仔细考虑;它通常表示设计有缺陷,因为...” * (3认同)
  • 虽然不可变值不应该被改变,但人们可能想要一个更新了单个索引的新值。如何方便地做到这一点可能并不明显,并提出这个问题。由于元组包含固定长度的数据,其中每个索引包含非常具体的值,因此类似于“namedtuple._replace”的便捷函数可能会有所帮助。 (2认同)

Dav*_*ter 60

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

>>> 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)
Run Code Online (Sandbox Code Playgroud)

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

  • 这就是对我最好的回答。我希望看到默认情况下在 python 中实现此功能,类似于“NamedTuple._replace(fieldname=fieldvalue)”:“tuple._replace(fieldindex, fieldvalue)” (2认同)

sja*_*obi 18

好吧,正如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:]
   ...:
Run Code Online (Sandbox Code Playgroud)

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

事实证明,对于短元组(在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
Run Code Online (Sandbox Code Playgroud)

然而,如果我们看一下更长的元组,列表转换是要走的路:

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

所以:如果你的元组很短,切片和连接.如果它很长,请进行列表转换!


Sup*_*ova 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')
Run Code Online (Sandbox Code Playgroud)


bph*_*phi 6

并不是说这是优越的,但是如果任何人好奇的话,可以通过以下方法完成:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])
Run Code Online (Sandbox Code Playgroud)

  • 这比 ```tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))``` 更快吗?(为什么不使用生成器理解呢?) (2认同)

fug*_*ede 6

我相信这从技术上讲可以回答问题,但不要在家中这样做。目前,所有答案都涉及创建一个新的元组,但是您可以使用它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
Run Code Online (Sandbox Code Playgroud)

  • 知道一些与通常答案不同的东西总是很高兴的。干杯! (2认同)
  • 您忘记将引用计数减少到您要丢弃的值。那就会造成漏电。 (2认同)

Bri*_*ing 5

它是使用idoimatic Python的简单方法:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])
Run Code Online (Sandbox Code Playgroud)

  • 以下是如何更改元组中的任何元素 - `i = 2; 值 = (*值[:i], '300', *值[i+1:])` (3认同)