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?

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

Jon Clements
fonte
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).

Dave Halter
fonte
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!

sjakobi
fonte
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:])
Brian Spiering
fonte
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)])
bphi
fonte
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
fuglede
fonte
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')
Super Nova
fonte
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")
Trufa
fonte
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.

OLIVER.KOO
fonte
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

Pooya
fonte
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.

BrenBarn
fonte
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.

Aedus
fonte
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')
GordanTrevis
fonte
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
Jason
fonte
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')
Unidade g
fonte
-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]
MC_Creep3r
fonte
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]
Rohit
fonte
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