Python: изменение значения в кортеже

124

Я новичок в python, поэтому этот вопрос может быть немного базовым. У меня есть кортеж, valuesкоторый содержит следующее:

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

Я хочу изменить первое значение (т.е. 275) в этом кортеже, но я понимаю, что кортежи неизменяемы, поэтому values[0] = 200работать не будет. Как я могу этого добиться?

Давуд
источник
24
кортежи неизменяемы , для этого вам нужно создать новый кортеж.
Хантер МакМиллен,

Ответы:

178

Сначала нужно спросить, зачем вам это нужно?

Но это возможно через:

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

Но если вам нужно что-то изменить, вам, вероятно, лучше оставить это как list

Джон Клементс
источник
4
Один из вариантов использования - это хранение большого количества небольших последовательностей, где значения редко меняются, но в некоторых случаях они могут захотеть это сделать. Для небольшой, но ненулевой последовательности, потребление памяти кортежем (60 байтов для одного элемента) по сравнению со списком (104 байта) и имеет значение. Другой вариант использования - для namedtuples, поскольку namedlist изначально не существует.
Майкл Скотт Катберт,
82
Ответы «почему вы хотите это сделать?» Сводят меня с ума на StackOverflow. Не думайте, что оригинальный постер "хочет" этого. Собственно, лучше не предполагать, что оригинальный плакат все это создает с нуля, ничего лучше. Чаще мы имеем дело с выводом из другого модуля или источника данных в формате или типе, которые мы не можем контролировать.
rtphokie 06
9
@rtphokie, желающий изменить неизменяемый контейнер (поэтому вопрос «почему» - это очень правильный вопрос), отличается от интерпретации различных форматов, некоторые из которых могут быть кортежами. Спасибо за высказывание :)
Джон Клементс
7
Преобразование в список и использование временной переменной кажется ненужным и неэффективным с точки зрения памяти. Вы можете просто распаковать в кортеж с тем же именем и при распаковке обновить все, что нужно обновить.
Брайан Спиринг
5
@JonClements Вы очень ценно отметили, что это плохая практика. Это должно быть в вашем ответе. Однако риторические вопросы часто интерпретируются как излишне унизительные. Такую информацию лучше структурировать в форме: «Это плохая практика, потому что ...» или даже «Подумайте, действительно ли вам это нужно; обычно это подразумевает ошибку в конструкции, потому что ...»
Филип Коулинг,
74

В зависимости от вашей проблемы нарезка может быть действительно отличным решением:

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

Это позволяет вам добавлять несколько элементов или также заменять несколько элементов (особенно, если они являются «соседями». В приведенном выше случае приведение к списку, вероятно, более уместно и читабельно (хотя обозначение срезов намного короче).

Дэйв Холтер
источник
24

Как уже показал Труфа, есть два основных способа заменить элемент кортежа по заданному индексу. Либо преобразуйте кортеж в список, замените элемент и преобразуйте обратно, либо создайте новый кортеж путем конкатенации.

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

Итак, какой метод лучше, то есть быстрее?

Оказывается, для коротких кортежей (в Python 3.3) конкатенация на самом деле быстрее!

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

Тем не менее, если мы посмотрим на более длинные кортежи, преобразование списка - это путь:

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

Для очень длинных кортежей преобразование списков значительно лучше!

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

Кроме того, производительность метода конкатенации зависит от индекса, по которому мы заменяем элемент. Для метода списка индекс не имеет значения.

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

Итак: если ваш кортеж короткий, нарежьте и объедините. Если он длинный, сделайте преобразование списка!

sjakobi
источник
1
@ErikAronesty не всегда. Когда-то полезным случаем является расширение класса, который вы не можете изменить и методы которого возвращают кортеж, из которого вы хотите изменить только первый элемент. return (val,) + res [1:] яснее, чем res2 = list (res); res2 [0] = val; return tuple (res2)
yucer
9

Возможно с одним лайнером:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])
Брайан Спиринг
источник
1
Как бы вы изменили в этом только третий элемент values?
sdbbs
2
Вот как вы можете изменить любой элемент в кортеже -i = 2; values = (*values[:i], '300', *values[i+1:])
Брайан Спиринг
8

Не то чтобы это лучше, но, если кому-то интересно, это можно сделать в одной строке:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])
bphi
источник
Это быстрее чем tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Почему не генератор понимания?)
Anonymous
8

Я считаю, что это технически ответ на вопрос, но не делайте этого дома. На данный момент все ответы включают создание нового кортежа, но вы можете использовать его ctypesдля изменения кортежа в памяти. Опираясь на различные детали реализации CPython в 64-битной системе, можно сделать это следующим образом:

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
Всегда полезно знать что-то отличное от обычных ответов. Ура!
Kartheek Palepu
Люди, которые хотят писать код на C, вероятно, должны делать именно это. Взлом такого интерпретатора просто упускает из виду эту тему. Это также ненадежно, поскольку детали реализации cPython могут измениться в любое время без предупреждения и могут нарушить любой код, который полагается на неизменяемые кортежи. Кроме того, кортежи - это самые легкие объекты коллекций в Python, поэтому нет проблем с созданием нового. Если вам абсолютно необходимо часто изменять коллекцию, используйте вместо этого список.
Bachsau
1
Вы забыли уменьшить счетчик ссылок до значения, которое вы отбрасываете. Это вызовет утечку.
wizzwizz4
6

Как написал в комментариях Хантер Макмиллен, кортежи неизменяемы, для этого вам нужно создать новый кортеж. Например:

>>> 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
источник
3

РЕДАКТИРОВАТЬ: это еще не работает с кортежами с повторяющимися записями !!

По идее Пуйи :

Если вы планируете делать это часто (чего не следует делать, поскольку кортежи по какой-то причине неизменяемы), вам следует сделать что-то вроде этого:

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

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

Или на основе идеи Джона :

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

print modTupByIndex((1,2,3),1,"a")
Trufa
источник
3

Фрист, спросите себя, почему вы хотите изменить свой tuple. Есть причина, по которой строки и кортеж неизменяемы в Ptyhon , если вы хотите изменить свой, tupleто, вероятно, listвместо этого он должен быть .

Во-вторых, если вы все еще хотите изменить свой кортеж, вы можете преобразовать его tupleв a, listзатем преобразовать его обратно и переназначить новый кортеж той же переменной. Это замечательно, если вы собираетесь изменить кортеж только один раз . В противном случае я лично считаю, что это нелогично. Потому что он по сути создает новый кортеж, и каждый раз, если вы хотите изменить кортеж, вам придется выполнять преобразование. Также, если вы читаете код, было бы непонятно, почему бы просто не создать list? Но это приятно, потому что не требует никакой библиотеки.

Я предлагаю использовать mutabletuple(typename, field_names, default=MtNoDefault)from mutabletuple 0.2 . Я лично считаю, что этот способ более интуитивно понятен и удобочитаем. Личное чтение кода должно знать, что писатель намеревается изменить этот кортеж в будущем. Обратной стороной по сравнению с listописанным выше методом преобразования является то, что вам необходимо импортировать дополнительный файл py.

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 : Не пытайтесь мутировать tuple. если вы это сделаете, и это будет одноразовая операция, преобразовать tupleв список, изменить его, превратить listв новый tupleи переназначить обратно переменной, содержащей старый tuple. Если вы tupleхотите чего-то избежать listи хотите видоизменить более одного раза, тогда создавайте mutabletuple.

OLIVER.KOO
источник
2

на основе Идеи Джона и дорогой Труфа

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

он изменяет все ваши старые вхождения значений

Pooya
источник
Это было бы довольно неудобно (из-за отсутствия лучшего слова), если бы вы изменили несколько значений, но опять же, зачем вам вообще изменять кортеж ...
Trufa
@Trufa да, я пытаюсь это написать: D
Pooya
Имя метода modify_tuple_by_index неточно и может вызвать путаницу.
msw
1

Вы не можете. Если вы хотите его изменить, вам нужно использовать список вместо кортежа.

Обратите внимание, что вместо этого вы можете создать новый кортеж с новым значением в качестве первого элемента.

BrenBarn
источник
0

Я обнаружил, что лучший способ редактировать кортежи - это воссоздать кортеж, используя предыдущую версию в качестве основы.

Вот пример, который я использовал для создания более светлой версии цвета (у меня он уже был открыт в то время):

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

Что он делает, так это то, что он просматривает «цвет» кортежа и читает каждый элемент, что-то делает с ним и, наконец, добавляет его в новый кортеж.

Так что вам нужно что-то вроде:

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

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

Этот конкретный вариант не работает, но концепция - это то, что вам нужно.

tuple = (0, 1, 2)

tuple = перебирать кортеж, при необходимости изменять каждый элемент

это концепция.

Aedus
источник
0

Я опаздываю в игру, но я думаю, что самый простой , экономичный и быстрый способ (в зависимости от ситуации) - это перезаписать сам кортеж. Так как это избавит от необходимости создавать список и переменную и архивируется в одну строку.

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

>>> (1, 2, 24)

Но: это удобно только для довольно небольших кортежей, а также ограничивает вас фиксированным значением кортежа, тем не менее, в любом случае это происходит с кортежами большую часть времени.

В данном конкретном случае это будет выглядеть так:

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
источник
это работает, только если это статический кортеж с известной длиной. not code, скорее всего, рано или поздно выйдет из строя, потому что он слишком специфичен ...
patroqueeet
да - @patroqueeet, поэтому минусы такого подхода я четко обозначил, после Но: ...-. Пожалуйста, пересмотрите свой отрицательный голос, спасибо;)
GordanTrevis
1
эй, пересмотрел и нажал кнопку. но голосование теперь заблокировано SO: /
patroqueeet
0

tldr; «обходным путем» является создание нового объекта кортежа, а не изменение исходного

Хотя это очень старый вопрос, кто-то рассказал мне об этом безумии изменения кортежей Python. Что я был очень удивлен / заинтригован, и, немного погуглил, я попал сюда (и другие похожие образцы в Интернете)

Я провел тест, чтобы доказать свою теорию

Примечание ==оценивает равенство, в то время isкак ссылочное равенство (obj a тот же экземпляр, что и 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))

Урожайность:

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

Вы не можете изменять элементы в кортеже, но можете изменять свойства изменяемых объектов в кортежах (например, если эти объекты являются списками или фактическими объектами класса)

Например

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

я сделал это:

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

и чтобы изменить, просто сделай

list[0]=6

и вы можете изменить кортеж: D

вот оно в точности скопировано из 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
источник
2
кортеж - это список, а не кортеж. x = (y)ничего не делает, но присваивает y значению x.
m4tx
2
Кроме того, используя имена "кортеж" и "список" в качестве переменных, вы затрудняете сравнение типов кортежей и списков позже.
Майкл Скотт Катберт,
-4

Вы можете изменить значение кортежа, используя копию по ссылке

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

>>> print(tuple1)
    [20, 10, 40]
Рохит
источник
1
Только то, что это списки, а не кортежи, которые вы можете изменить в любом случае, учитывая вторую ссылку или нет.
Bachsau