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 байти) і внесе значення. Інший випадок використання для nametuples, оскільки позначений список не існує.
Майкл Скотт Катберт

81
Відповіді "чому ти хочеш це зробити" наштовхують мене на StackOverflow. Не припускайте, що оригінальний плакат "хочу" зробити це. Насправді, краще не вважати, що оригінальний плакат створює все це з нуля, не знає нічого кращого. Частіше ми маємо справу з результатами з іншого модуля чи джерела даних у форматі чи типі, якими ми не можемо керувати.
rtphokie

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

Ну, як Trufa вже показав, в основному є два способи заміни елемента кортежа на заданий індекс. Або перетворіть кортеж у список, замініть елемент і перетворіть назад, або побудуйте новий кортеж шляхом конкатенації.

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 = список (res); res2 [0] = val; повернути кортеж (res2)
юсер

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))? (Чому б не зрозуміти генератор?)
Анонім

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 можуть змінюватися в будь-який час без попередження і, ймовірно, порушують будь-який код, який покладається на кортежі, які не змінюються. Крім того, кортежі - це найлегші предмети колекції в пітоні, тому зі створенням нового не виникає проблем. Якщо вам абсолютно потрібно часто змінювати колекцію, замість цього використовуйте список.
Бахсау

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

EDIT: Це ще не працює з кортежами з повторюваними записами !!

Виходячи з ідеї Pooya :

Якщо ви плануєте робити це часто (чого не слід, оскільки кортежі є незмінними з причини), вам слід зробити щось подібне:

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

Frist, запитайте себе, чому ви хочете мутувати своє tuple. Існує причина, чому рядки та кортеж незмінні в Ptyhon , якщо ви хочете мутувати свій, tupleто він, ймовірно, повинен бути listзамість цього.

По-друге, якщо ви все ще бажаєте мутувати свій кортеж, ви можете перетворити його tupleна listпотім перетворити його назад і перепризначити новий кортеж тій самій змінній. Це чудово, якщо ви мутируєте свій кортеж лише один раз . В іншому випадку я особисто думаю, що це контрінтуїтивно. Тому що це по суті створення нового кортежу, і кожного разу, якщо ви бажаєте мутувати кортеж, вам доведеться здійснити перетворення. Крім того, якщо ви читаєте код, було б заплутано думати, чому б не просто створити list? Але це приємно, бо не потребує жодної бібліотеки.

Я пропоную використовувати mutabletuple(typename, field_names, default=MtNoDefault)з 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 так, я намагаюся написати це: 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)

кортеж = повторіть кортеж, змінюйте кожен елемент за потребою

ось ця концепція.


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

це працює лише, якщо це статичний кортеж із відомою довжиною. не код, швидше за все, вийде з ладу рано чи пізно, оскільки його занадто специфічний ...
patroqueeet

так - @patroqueeet, Тому я чітко заявив про недоліки такого підходу, але після: ...-. Перегляньте свій запис, дякую;)
GordanTrevis

1
ей, переосмислив і натиснув кнопку. але голосування зараз заблоковано SO: /
patroqueeet

0

tldr; "вирішення" створює новий об'єкт кортежу, фактично не змінюючи оригінал

Хоча це дуже давнє запитання, хтось розповів мені про це безумство кортежів Python. Що мене дуже здивувало / заінтригувало, і роблячи якісь гугли, я приземлився тут (та інші подібні зразки в Інтернеті)

Я провів тест, щоб довести свою теорію

Примітка ==робить рівність значення, а isреферентна рівність (є obj тим же екземпляром, що і 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
Тільки те, що це списки, а не кортежі, які ви все одно можете змінити, надаючи другу посилання чи ні.
Бахсау
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.