4

I know I can't modify a tuple and I've seen ways to create a tuple from another one concatenating parts of the original manually like here.

But wonder whether there has emerged some pythonic way to 'modify' a tuple by implicitly creating a new one like

>>> source_tuple = ('this', 'is', 'the', 'old', 'tuple')
>>> new_tuple = source_tuple.replace(3, 'new')
>>> new_tuple
('this', 'is', 'the', 'new', 'tuple')

A possible implementation could look like this but I'm looking for a built in solution:

def replace_at(source, index, value):
    if isinstance(source, tuple):
        return source[:index] + (value,) + source[index + 1:]
    elif isinstance(source, list):
        return source[:index] + [value,] + source[index + 1:]
    else:
        explode()

it's not much work to implement such a functionality but like the Enum has demonstrated it's sometimes better to have an implementation everyone uses..

Edit: my goal is not to replace the source tuple. I know I could use lists but even in this case I would make a copy first. So I'm really just looking for a way to create a modified copy.

Community
  • 1
  • 1
frans
  • 8,868
  • 11
  • 58
  • 132

4 Answers4

6

You can use a slice on the tuple (which yields a new tuple) and concatenate:

>>> x=3
>>> new_tuple=source_tuple[0:x]+('new',)+source_tuple[x+1:]
>>> new_tuple
('this', 'is', 'the', 'new', 'tuple')

Which you can then support either a list or tuple like so:

>>> def replace_at(source, index, value):
...     return source[0:index]+type(source)((value,))+source[index+1:]
...
>>> replace_at([1,2,3],1,'new')
[1, 'new', 3]
>>> replace_at((1,2,3),1,'new')
(1, 'new', 3)

Or, just do it directly on a list:

>>> source_tuple = ('this', 'is', 'the', 'old', 'tuple')
>>> li=list(source_tuple)
>>> li[3]='new'
>>> new_tuple=tuple(li)
>>> new_tuple
('this', 'is', 'the', 'new', 'tuple')

As stated in the comments -- that is what lists are for...

dawg
  • 98,345
  • 23
  • 131
  • 206
  • You can do it like this of course but you have to admit that this code is neither short nor intuitive since it's three lines of code with two type casts just saying "replace element 3 with 'new'" – frans Sep 21 '16 at 21:02
  • Try a slice then: `new_tuple=source_tuple[0:3]+('new',)+source_tuple[4:]` – dawg Sep 21 '16 at 21:07
  • This is what I wrote :) – frans Sep 21 '16 at 21:09
  • Rather than the `if/then` you can do: `source[0:index]+type(source)((value,))+source[index+1:]` – dawg Sep 21 '16 at 21:17
  • This is not exactly what I was hoping to find but you can't change Python for me :). Your answer demonstrates nice approaches so I'll choose it! – frans Sep 21 '16 at 21:38
3

If you're thinking of swapping values on the fly, then a list is the more appropriate data structure; as we already know tuples are immutable.

On another note, if you're looking for a swap-value logic in a tuple, you can have a look at collections.namedtuple which has a _replace method.

They can be used wherever regular tuples are used

>>> source_tuple = ('this', 'is', 'the', 'old', 'tuple')

>>> Factory = namedtuple('Factory', range(5), rename=True)

>>> source_tuple = Factory(*source_tuple)

>>> source_tuple
Factory(_0='this', _1='is', _2='the', _3='old', _4='tuple')

>>> new_tuple = source_tuple._replace(_3='new')

>>> new_tuple
Factory(_0='this', _1='is', _2='the', _3='new', _4='tuple')

Well that doesn't look too elegant. I still suggest you use a list instead.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
0

You could use some kind of comprehension:

source_tuple = ('this', 'is', 'the', 'old', 'tuple')
new_tuple = tuple((value if x != 3 else 'new'
                   for x, value in enumerate(source_tuple)))
# ('this', 'is', 'the', 'new', 'tuple')

This is rather idotic in this case but gives you an idea of the general concept. Better use a list, though, after all, you can change the values index-based here.

Jan
  • 42,290
  • 8
  • 54
  • 79
0

If you need to create new tuple with replaced element, you may use something like this:

def replace_value_in_tuple(t, ind, value):
    return tuple(
        map(lambda i: value if i == ind else t[i], range(len(t)))
    )
ShabashP
  • 578
  • 2
  • 10