2014-04-27 51 views
2

这很可能是一些东西的重复,但我的serach foo让我失望。什么是“改变”元组元素的优雅方式?

它是众所周知的元组是不可变的,所以你不能真的改变它们。然而,有时候,可能需要做一些改变的工作,比如(1, 2, "three")(1, 2, 3),或许与Haskell record update syntax.类似,但实际上并没有改变原始元组,但是你会得到一个新的只有一个(或更多)元素不同。

的一种方式去这样做,这将是:

elements = list(old_tuple) 
elements[-1] = do_things_to(elements[-1]) 
new_tuple = tuple(elements) 

我觉得改变一个元组列表,但是那种违背了使用的元组类型old_tuple开始与目的:如果你是使用列表来代替,您不必在每个操作的内存中构建元组丢弃列表副本。

如果你要改变,比方说,只是一个元组的第三个元素,你也可以这样做:

def update_third_element(a, b, c, *others): 
    c = do_things_to(c) 
    return tuple(a, b, c, *others) 

new_tuple = update_third_element(*old_tuple) 

这将抵制在元组中的元素数量的变化比幼稚的做法更好:

a, b, c, d, e, f, g, h, i, j = old_tuple 
c = do_things_to(c) 
new_tuple = (a, b, c, d, e, f, g, h, j, j) # whoops 

...但它不工作,如果你想改变是最后一个,或ñ列到最后一个元素。它还创建一个扔掉列表(others)。它也迫使你命名所有元素,最多为n -th。

有没有更好的方法?

+3

你说,“改变一个元组的列表但是那种违背了使用的元组类型的宗旨,为'old_tuple'到首先”。事实上,需要改变一个元素就是破坏了使用元组类型开始的目的。如果你发现自己需要改变元组的元素,他们可能不应该首先是元组。如果你不需要经常这样做,它可能会有点难看。 – BrenBarn

+0

@BrenBarn我不同意不可变性(如果你持有这个对象的引用,它永远不会改变)意味着你不应该想要一个对象,并从中创建一个与原始对象略有不同的新对象。 – badp

+0

对于那些不相信的人来说,这个最好的例子当然是'str's,它有一个'替换'的方法,尽管它是不可变的。 – badp

回答

2

我会用collections.namedtuple代替:

>>> from collections import namedtuple 

>>> class Foo(namedtuple("Foo", ["a", "b", "c"])): 
     pass 

>>> f = Foo(1, 2, 3) # or Foo(a=1, b=2, c=3) 
>>> f._replace(a = 5) 
Foo(a=5, b=2, c=3) 

namedtuple S还支持索引,所以你可以将普通的元组的使用它们。

如果你必须使用一个普通的元组,只需使用一个辅助功能:

>>> def updated(tpl, i, val): 
     return tpl[:i] + (val,) + tpl[i + 1:] 

>>> tpl = (1, 2, 3) 
>>> updated(tpl, 1, 5) 
(1, 5, 3) 
+0

+1:'_replace'本质上是我正在寻找的元组方法;由于某种原因,'tuple'实际上并没有它。 – badp

相关问题