Python: cambio de valor en una tupla

124

Soy nuevo en Python, por lo que esta pregunta podría ser un poco básica. Tengo una tupla llamada valuesque contiene lo siguiente:

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

Quiero cambiar el primer valor (es decir, 275) en esta tupla, pero entiendo que las tuplas son inmutables, por values[0] = 200lo que no funcionarán. ¿Cómo puedo conseguir esto?

Dawood
fuente
24
Las tuplas son inmutables , debe crear una nueva tupla para lograrlo.
Hunter McMillen

Respuestas:

178

Primero debes preguntar, ¿por qué quieres hacer esto?

Pero es posible a través de:

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

Pero si va a necesitar cambiar las cosas, probablemente sea mejor que lo mantenga como list

Jon Clements
fuente
4
Un caso de uso es si está almacenando una gran cantidad de secuencias pequeñas donde los valores rara vez cambian, pero en pocas ocasiones podrían querer hacerlo. Para una secuencia de longitud pequeña pero distinta de cero, el consumo de memoria de la tupla (60 bytes para un elemento) frente a la lista (104 bytes) y marca la diferencia. Otro caso de uso es para namedtuples ya que namedlist no existe de forma nativa.
Michael Scott Cuthbert
81
Las respuestas de "¿por qué quieres hacer eso?" Me vuelven loco en StackOverflow. No asuma que el cartel original "quiere" hacer eso. En realidad, es mejor no asumir que el póster original está creando todo esto desde cero, no lo sabe nada mejor. Más a menudo estamos tratando con la salida de otro módulo o fuente de datos en un formato o tipo que no podemos controlar.
rtphokie
9
@rtphokie querer mutar un contenedor inmutable (por lo tanto, el "por qué" es una pregunta muy válida) es diferente a interpretar diferentes formatos, algunos de los cuales pueden ser una tupla. Gracias por desahogarse :)
Jon Clements
7
Parece innecesario y la memoria ineficaz volver a convertir en una lista y utilizar una variable temporal. Puede descomprimirlo en una tupla con el mismo nombre y, mientras desempaqueta, actualice lo que necesite actualizar.
Brian Spiering
5
@JonClements Hace un punto muy valioso de que hacer esto es una mala práctica. Eso debería estar en tu respuesta. Sin embargo, las preguntas retóricas a menudo se interpretan como innecesariamente despectivas. Dicha información está mejor estructurada en la forma: "Esta es una mala práctica porque ..." o incluso "Considere cuidadosamente si realmente necesita esto; por lo general, implica una falla en el diseño porque ..."
Philip Couling
74

Dependiendo de su problema, cortar en rodajas puede ser una buena solución:

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

Esto le permite agregar varios elementos o también reemplazar algunos elementos (especialmente si son "vecinos". En el caso anterior, la conversión a una lista es probablemente más apropiada y legible (aunque la notación de corte es mucho más corta).

Dave Halter
fuente
24

Bueno, como Trufa ya ha mostrado, hay básicamente dos formas de reemplazar el elemento de una tupla en un índice dado. Convierta la tupla en una lista, reemplace el elemento y vuelva a convertir, o construya una nueva tupla por concatenación.

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:]
   ...:

Entonces, ¿qué método es mejor, es decir, más rápido?

¡Resulta que para tuplas cortas (en Python 3.3), la concatenación es en realidad más rápida!

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

Sin embargo, si observamos tuplas más largas, la conversión de listas es el camino a seguir:

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

Para tuplas muy largas, la conversión de listas es sustancialmente mejor.

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

Además, el rendimiento del método de concatenación depende del índice en el que reemplazamos el elemento. Para el método de lista, el índice es irrelevante.

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

Entonces: si su tupla es corta, corte y concatene. Si es largo, ¡haz la conversión de la lista!

sjakobi
fuente
1
@ErikAronesty no siempre. Una vez, el caso útil es extender una clase que no se puede cambiar y cuyos métodos devuelven una tupla de la que se desea cambiar solo el primer elemento. return (val,) + res [1:] es más claro que res2 = list (res); res2 [0] = val; Tupla de retorno (res2)
Yucer
9

Es posible con una sola línea:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])
Brian Spiering
fuente
1
¿Cómo cambiarías solo el tercer elemento valuescon esto?
sdbbs
2
Así es como se puede cambiar cualquier elemento en una tuplai = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering
8

No es que esto sea superior, pero si alguien tiene curiosidad se puede hacer en una línea con:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])
bphi
fuente
¿Es eso más rápido que tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (¿Por qué no la comprensión del generador?)
Anónimo
8

Creo que técnicamente esto responde a la pregunta, pero no hagas esto en casa. Por el momento, todas las respuestas implican la creación de una nueva tupla, pero puede usarla ctypespara modificar una tupla en la memoria. Basándose en varios detalles de implementación de CPython en un sistema de 64 bits, una forma de hacerlo es la siguiente:

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
fuglede
fuente
1
Siempre es bueno saber algo diferente a las respuestas habituales. Salud !
Kartheek Palepu
Las personas que quieran codificar en C probablemente deberían hacer exactamente eso. Hackear al intérprete de esa manera simplemente pierde el tema aquí. Tampoco es confiable, ya que los detalles de implementación de cPython pueden cambiar en cualquier momento sin previo aviso y es probable que rompa cualquier código que se base en tuplas que son imutables. Además, las tuplas son los objetos de colección más livianos en Python, por lo que no hay problema para crear uno nuevo. Si es absolutamente necesario modificar una colección con frecuencia, utilice una lista en su lugar.
Bachsau
1
Olvidó disminuir el recuento de referencias al valor que está descartando. Eso causará fugas.
wizzwizz4
6

Como escribió Hunter McMillen en los comentarios, las tuplas son inmutables, es necesario crear una nueva tupla para lograr esto. Por ejemplo:

>>> 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')
SuperNova
fuente
3

EDITAR: ¡¡Esto todavía no funciona en tuplas con entradas duplicadas !!

Basado en la idea de Pooya :

Si planeas hacer esto a menudo (lo cual no deberías ya que las tuplas son inmutables por una razón), debes hacer algo como esto:

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

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

O basado en la idea de Jon :

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

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

Primero, pregúntese por qué quiere mutar su tuple. Hay una razón por la que las cadenas y la tupla son inmutables en Ptyhon , si desea mutar su, tupleentonces probablemente debería ser a list.

En segundo lugar, si aún desea mutar su tupla, puede convertir su tuplea, listluego volver a convertirla y reasignar la nueva tupla a la misma variable. Esto es genial si solo vas a mutar tu tupla una vez . De lo contrario, personalmente creo que es contradictorio. Porque esencialmente está creando una nueva tupla y cada vez que desee mutar la tupla, tendrá que realizar la conversión. Además, si lee el código, sería confuso pensar ¿por qué no simplemente crear un list? Pero es bueno porque no requiere ninguna biblioteca.

Sugiero usar mutabletuple(typename, field_names, default=MtNoDefault)desde mutabletuple 0.2 . Personalmente creo que esta forma es más intuitiva y legible. La persona que lea el código sabrá que el escritor tiene la intención de mutar esta tupla en el futuro. La desventaja en comparación con el listmétodo de conversión anterior es que esto requiere que importe un archivo py adicional.

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 : No intentes mutar tuple. si lo hace y es una operación de una sola vez, conviértala tupleen lista, muétela, conviértala listen una nueva tupley reasigne de nuevo a la variable que contiene la antigua tuple. Si desea tupley de alguna manera quiere evitar listy quiere mutar más de una vez, cree mutabletuple.

OLIVER.KOO
fuente
2

basado en la Idea de Jon y la querida 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")

cambia todas las ocurrencias de valores antiguos

Pooya
fuente
Esto sería bastante incómodo (por la falta de una palabra mejor) si tuviera que cambiar varios valores, pero, de nuevo, ¿por qué querría modificar una tupla en primer lugar ...
Trufa
@Trufa sí, estoy tratando de escribirlo: D
Pooya
El nombre del método modified_tuple_by_index es inexacto y puede causar confusión.
msw
1

No puedes. Si desea cambiarlo, debe usar una lista en lugar de una tupla.

Tenga en cuenta que, en cambio, podría crear una nueva tupla que tenga el nuevo valor como primer elemento.

BrenBarn
fuente
0

He descubierto que la mejor manera de editar tuplas es recrear la tupla usando la versión anterior como base.

Aquí hay un ejemplo que usé para hacer una versión más clara de un color (ya lo tenía abierto en ese momento):

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

Lo que hace es pasar por el 'color' de la tupla y leer cada elemento, hacer algo y finalmente agregarlo a la nueva tupla.

Entonces, lo que querrías sería algo como:

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

values  = (tuple(for i in values: if i = 0: i = 200 else i = values[i])

Ese específico no funciona, pero el concepto es lo que necesitas.

tuple = (0, 1, 2)

tupla = iterar a través de la tupla, modificar cada elemento según sea necesario

ese es el concepto.

Aedus
fuente
0

Llego tarde al juego, pero creo que la forma más sencilla , fácil de usar y rápida (dependiendo de la situación) es sobrescribir la propia tupla. Dado que esto eliminaría la necesidad de la creación de listas y variables y se archiva en una línea.

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

>>> (1, 2, 24)

Pero: Esto solo es útil para tuplas bastante pequeñas y también lo limita a un valor de tupla fijo, sin embargo, este es el caso de las tuplas la mayor parte del tiempo.

Entonces, en este caso particular, se vería así:

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')
Jordán Trevis
fuente
esto solo funciona si se trata de una tupla estática con una longitud conocida. lo más probable es que el código no falle tarde o temprano porque es demasiado específico ...
patroqueeet
sí - @patroqueeet, Por lo tanto, expuse claramente las desventajas de este enfoque, después de Pero: ...-. Por favor reconsidere su
voto negativo
1
hey, reconsiderado y pulsado el botón. pero la votación ahora está bloqueada por SO: /
patroqueeet
0

tldr; la "solución alternativa" es crear un nuevo objeto de tupla, no modificar el original

Si bien esta es una pregunta muy antigua, alguien me habló de esta locura de tuplas mutantes de Python. Lo cual me sorprendió / intrigó mucho, y al buscar en Google, aterricé aquí (y otras muestras similares en línea)

Hice una prueba para probar mi teoría

Note ==valora la igualdad mientras isque la igualdad referencial (es obj a la misma instancia que 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))

Rendimientos:

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
Jason
fuente
0

No puede modificar elementos en tuplas, pero puede modificar propiedades de objetos mutables en tuplas (por ejemplo, si esos objetos son listas u objetos de clase reales)

Por ejemplo

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')
gunit
fuente
-2

hice esto:

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

y para cambiar, solo hazlo

list[0]=6

y puedes cambiar una tupla: D

aquí está copiado exactamente de 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]
MC_Creep3r
fuente
2
tupla es una lista, no una tupla. x = (y)no hace nada más que asigna y a x.
m4tx
2
Además, al usar el nombre "tupla" y "lista" como variables, será difícil comparar los tipos de tuplas y listas más adelante.
Michael Scott Cuthbert
-4

Puede cambiar el valor de la tupla usando copiar por referencia

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

>>> print(tuple1)
    [20, 10, 40]
Rohit
fuente
1
Solo que estas son listas, no tuplas, que puedes cambiar de todos modos, con una segunda referencia o no.
Bachsau