Python: alterando valor em uma tupla


123

Eu sou novo em python, então esta questão pode ser um pouco básica. Eu tenho uma tupla chamada valuesque contém o seguinte:

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

Quero alterar o primeiro valor (ou seja, 275) nesta tupla, mas entendo que as tuplas são imutáveis, portanto values[0] = 200não funcionarão. Como posso conseguir isso?


24
Como as tuplas são imutáveis , é necessário criar uma nova tupla para conseguir isso.
Hunter McMillen

Respostas:


177

Primeiro você precisa perguntar, por que você quer fazer isso?

Mas é possível via:

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

Mas se você precisar mudar as coisas, é melhor mantê-lo como um list


4
Um caso de uso é se você estiver armazenando um grande número de pequenas seqüências em que os valores raramente mudam, mas em algumas ocasiões eles podem querer. Para uma sequência de tamanho pequeno, mas diferente de zero, o consumo de memória da tupla (60 bytes para um elemento) versus a lista (104 bytes) e faz a diferença. Outro caso de uso é para nomeduplos, pois a lista nomeada não existe nativamente.
Michael Scott Cuthbert /

81
As respostas "por que você quer fazer isso" me deixam maluco no StackOverflow. Não presuma que o pôster original "queira" fazer isso. Na verdade, é melhor não supor que o pôster original esteja criando tudo isso do zero e que não conheça melhor. Mais frequentemente, estamos lidando com a saída de outro módulo ou fonte de dados em um formato ou tipo que não podemos controlar.
Rtphokie

9
@rtphokie quer mutar um contêiner imutável (portanto, o "porquê" é uma pergunta muito válida) é diferente de interpretar diferentes formatos, alguns dos quais podem ser uma tupla. Obrigado por ventilação embora :)
Jon Clements

7
Parece desnecessário e ineficiente de memória reformular uma lista e usar uma variável temporária. Você pode apenas descompactar em uma tupla com o mesmo nome e ao descompactar atualizar o que precisar de atualização.
Brian Spiering

5
@ JonClements Você afirma muito valiosamente que fazer isso é uma prática ruim. Isso deve estar na sua resposta. No entanto, questões retóricas são frequentemente interpretadas como desnecessariamente depreciativas. Essas informações estão melhor estruturadas no formato: "Isso é uma prática ruim porque ..." ou até "Considere com cuidado se você realmente precisa disso; geralmente implica uma falha no design porque ..."
Philip Couling

74

Dependendo do problema, o fatiamento pode ser uma solução realmente interessante:

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

Isso permite adicionar vários elementos ou também substituir alguns elementos (especialmente se forem "vizinhos". No caso acima, a conversão para uma lista é provavelmente mais apropriada e legível (mesmo que a notação de fatiamento seja muito menor).


24

Bem, como Trufa já mostrou, existem basicamente duas maneiras de substituir o elemento de uma tupla em um determinado índice. Converta a tupla em uma lista, substitua o elemento e converta novamente ou construa uma nova tupla por concatenação.

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

Então, qual método é melhor, ou seja, mais rápido?

Acontece que para tuplas curtas (no Python 3.3), a concatenação é realmente mais 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

No entanto, se observarmos as tuplas mais longas, a conversão da lista é o caminho 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 muito longas, a conversão de lista é substancialmente melhor!

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

Além disso, o desempenho do método de concatenação depende do índice no qual substituímos o elemento. Para o método de lista, o índice é 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

Portanto: se sua tupla for curta, corte e concatene. Se for longo, faça a conversão da lista!


1
@ErikAronesty nem sempre. Uma vez que o caso útil está estendendo uma classe que você não pode alterar e cujos métodos retornam uma tupla a partir da qual você deseja alterar apenas o primeiro elemento. return (val,) + res [1:] é mais claro que res2 = lista (res); res2 [0] = val; tupla de retorno (res2)
yucer

9

É possível com um liner:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])

1
Como você mudaria apenas o terceiro elemento valuescom isso?
Sdbbs 22/10/19

2
Aqui está como você pode alterar qualquer elemento de uma tupla -i = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering

8

Não que isso seja superior, mas se alguém estiver curioso, isso pode ser feito em uma linha com:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])

Isso é mais rápido que tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Por que não gerador compreensão?)
Anônimo

8

Acredito que isso responda tecnicamente à pergunta, mas não faça isso em casa. No momento, todas as respostas envolvem a criação de uma nova tupla, mas você pode usá-lo ctypespara modificar uma tupla na memória. Contando com vários detalhes de implementação do CPython em um sistema de 64 bits, uma maneira de fazer isso é a seguinte:

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
É sempre bom saber algo diferente das respostas usuais. Felicidades !
Kartheek Palepu

As pessoas que desejam codificar em C provavelmente devem fazer exatamente isso. Hackear o intérprete assim perde o tópico aqui. Também não é confiável, já que os detalhes da implementação do cPython podem mudar a qualquer momento sem aviso e provavelmente quebram qualquer código que dependa de tuplas sendo imutáveis. Além disso, as tuplas são os objetos de coleção mais leves do python, portanto não há problema em criar um novo. Se você absolutamente precisar modificar uma coleção com freqüência, use uma lista.
Bachsau

1
Você esqueceu de diminuir a contagem de referência para o valor que está descartando. Isso causará vazamento.
precisa saber é o seguinte

6

Como Hunter McMillen escreveu nos comentários, as tuplas são imutáveis, você precisa criar uma nova tupla para conseguir isso. Por exemplo:

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

EDIT: Isso ainda não funciona em tuplas com entradas duplicadas !!

Com base na idéia de Pooya :

Se você planeja fazer isso com frequência (o que você não deve fazer, já que as tuplas são imutáveis ​​por um motivo), faça algo assim:

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

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

Ou com base na ideia de Jon :

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

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

3

Primeiro, pergunte a si mesmo por que você deseja alterar o seu tuple. Há uma razão pela qual cordas e tuplas são imutáveis ​​em Ptyhon ; se você deseja alterar o seu tuple, provavelmente deve ser um list.

Segundo, se você ainda deseja alterar sua tupla, pode convertê-lo tupleem um e listdepois convertê-lo novamente e reatribuir a nova tupla à mesma variável. Isso é ótimo se você só vai mudar sua tupla uma vez . Caso contrário, eu pessoalmente acho isso contra-intuitivo. Porque é essencialmente a criação de uma nova tupla e, sempre que você desejar alterar a tupla, você terá que realizar a conversão. Além disso, se você ler o código, seria confuso pensar por que não criar um list? Mas é legal porque não requer nenhuma biblioteca.

Eu sugiro usar mutabletuple(typename, field_names, default=MtNoDefault)do mutabletuple 0.2 . Pessoalmente, acho que esse caminho é mais intuitivo e legível. A leitura pessoal do código saberia que o escritor pretende alterar essa tupla no futuro. A desvantagem se compara ao listmétodo de conversão acima é que isso requer que você importe um arquivo 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 : Não tente fazer mutação tuple. se você fizer isso e for uma operação única, converta tuplepara lista, modifique-a, transforme-a listem uma nova tuplee reatribua-a novamente à variável antiga tuple. Se os desejos tuplee de alguma forma querem evitar liste querem mudar mais de uma vez, crie mutabletuple.


2

baseado na idéia de Jon e na 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")

altera todas as ocorrências de valores antigos


Isso seria bastante desconfortável (pela falta de uma palavra melhor) se você alterasse vários valores, mas, novamente, por que você desejaria modificar uma tupla em primeiro lugar ...
Trufa

@Trufa sim, eu estou tentando escrever que: D
Pooya

O nome do método modify_tuple_by_index é impreciso e está vinculado a causar confusão.
12/07

1

Você não pode. Se você quiser alterá-lo, precisará usar uma lista em vez de uma tupla.

Observe que você pode criar uma nova tupla que tenha o novo valor como seu primeiro elemento.


0

Encontrei a melhor maneira de editar tuplas é recriar a tupla usando a versão anterior como base.

Aqui está um exemplo que eu usei para criar uma versão mais clara de uma cor (eu já estava aberta na época):

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

O que ele faz é percorrer a "cor" da tupla e ler cada item, fazer alguma coisa e, finalmente, adicioná-lo à nova tupla.

Então, o que você gostaria seria 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])

Esse específico não funciona, mas o conceito é o que você precisa.

tuple = (0, 1, 2)

tupla = itera através da tupla, altera cada item conforme necessário

esse é o conceito.


0

Estou atrasado para o jogo, mas acho que a maneira mais simples , mais amigável e mais rápida de usar os recursos (dependendo da situação) , é sobrescrever a tupla em si. Como isso eliminaria a necessidade da criação de lista e variável e é arquivado em uma linha.

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

>>> (1, 2, 24)

Mas: isso é útil apenas para tuplas bastante pequenas e também limita você a um valor de tupla fixo; no entanto, esse é o caso das tuplas na maioria das vezes.

Portanto, nesse caso em particular, ficaria assim:

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

isso só funciona se for uma tupla estática com comprimento conhecido. Não código provavelmente irá falhar mais cedo ou mais tarde, porque a sua muito específico ...
patroqueeet

sim - @patroqueeet, Portanto, eu declarei claramente as desvantagens dessa abordagem, depois de Mas: ...-. Por favor, reconsidere seus downvote, graças;)
GordanTrevis

1
re-considerado e clicou no botão. mas o voto agora está bloqueado pelo SO: /
patroqueeet 14/02

0

tldr; a "solução alternativa" está criando um novo objeto de tupla, sem modificar o original

Embora essa seja uma pergunta muito antiga, alguém me contou sobre essa loucura das tuplas mutantes do Python. Fiquei muito surpreso / intrigado e, pesquisando no Google, cheguei aqui (e outras amostras semelhantes online)

Fiz alguns testes para provar minha teoria

Nota ==valoriza a igualdade enquanto igualdade isreferencial (obj é a mesma instância 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))

Rendimentos:

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

Você não pode modificar itens na tupla, mas pode modificar propriedades de objetos mutáveis ​​nas tuplas (por exemplo, se esses objetos são listas ou objetos de classe reais)

Por exemplo

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

eu fiz isso:

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

e para mudar, basta fazer

list[0]=6

e você pode alterar uma tupla: D

aqui está copiado exatamente do 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
tupla é uma lista, não uma tupla. x = (y)não faz nada, mas atribui y a x.
M4tx 01/05/19

2
Além disso, usando o nome "tupla" e "lista" como variáveis, será difícil comparar os tipos de tupla e lista posteriormente.
Michael Scott Cuthbert /

-4

Você pode alterar o valor da tupla usando copiar por referência

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

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

1
Só que estas são listas, não tuplas, que você pode alterar de qualquer maneira, dada uma segunda referência ou não.
Bachsau
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.