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

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


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

Это быстрее чем 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')

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

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.


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

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


Это было бы довольно неудобно (из-за отсутствия лучшего слова), если бы вы изменили несколько значений, но опять же, зачем вам вообще изменять кортеж ...
Trufa

@Trufa да, я пытаюсь это написать: D
Pooya

Имя метода modify_tuple_by_index неточно и может вызвать путаницу.
msw

1

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

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


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 = перебирать кортеж, при необходимости изменять каждый элемент

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


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

это работает, только если это статический кортеж с известной длиной. 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]

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.