Python: mengubah nilai dalam tupel

124

Saya baru mengenal python jadi pertanyaan ini mungkin sedikit mendasar. Saya memiliki tupel yang disebut valuesyang berisi yang berikut ini:

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

Saya ingin mengubah nilai pertama (yaitu 275) dalam tupel ini, tetapi saya memahami bahwa tupel tidak dapat diubah sehingga values[0] = 200tidak akan berfungsi. Bagaimana saya bisa mencapai ini?

Dawood
sumber
24
tupel tidak dapat diubah , Anda perlu membuat tupel baru untuk melakukannya.
Hunter McMillen

Jawaban:

178

Pertama, Anda perlu bertanya, mengapa Anda ingin melakukan ini?

Tapi itu mungkin melalui:

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

Tetapi jika Anda ingin mengubah sesuatu, Anda mungkin lebih baik menyimpannya sebagai file list

Jon Clements
sumber
4
Salah satu kasus penggunaan adalah jika Anda menyimpan sejumlah besar urutan kecil di mana nilainya jarang berubah tetapi pada beberapa kesempatan mereka mungkin menginginkannya. Untuk urutan panjang yang kecil tapi bukan nol, konsumsi memori tuple (60-byte untuk satu elemen) vs list (104 byte) dan membuat perbedaan. Kasus penggunaan lain adalah untuk namaiupel karena daftar nama tidak ada.
Michael Scott Cuthbert
81
Tanggapan "mengapa Anda ingin melakukan itu" membuat saya gila di StackOverflow. Jangan berasumsi bahwa pengepos asli "ingin" melakukan itu. Sebenarnya, lebih baik tidak berasumsi bahwa poster asli yang membuat semua ini dari awal tidak tahu apa-apa. Lebih sering kita berurusan dengan keluaran dari modul atau sumber data lain dalam format atau tipe yang tidak dapat kita kendalikan.
rtphokie
9
@rtphokie ingin mengubah wadah yang tidak dapat diubah (oleh karena itu "mengapa" adalah pertanyaan yang sangat valid) berbeda dengan menafsirkan format yang berbeda beberapa di antaranya mungkin berupa tupel. Terima kasih telah melampiaskannya :)
Jon Clements
7
Tampaknya tidak perlu dan memori tidak efisien untuk menyusun ulang menjadi daftar dan menggunakan variabel sementara. Anda bisa membongkar menjadi tupel dengan nama yang sama dan saat membongkar pembaruan apa pun yang perlu diperbarui.
Brian Spiering
5
@JonClements Anda membuat poin yang sangat berharga bahwa melakukan ini adalah praktik yang buruk. Itu harus menjadi jawaban Anda. Namun pertanyaan retoris sering diartikan sebagai penghinaan yang tidak perlu. Informasi tersebut lebih baik terstruktur dalam bentuk: "Ini adalah praktik yang buruk karena ..." atau bahkan "Pertimbangkan baik-baik jika Anda benar-benar membutuhkan ini; biasanya menyiratkan kesalahan dalam desain karena ..."
Philip Couling
74

Bergantung pada masalah Anda, mengiris bisa menjadi solusi yang sangat rapi:

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

Hal ini memungkinkan Anda untuk menambahkan beberapa elemen atau juga untuk mengganti beberapa elemen (terutama jika mereka adalah "tetangga". Dalam kasus di atas, casting ke daftar mungkin lebih tepat dan dapat dibaca (meskipun notasi pemotongan jauh lebih pendek).

Dave Halter
sumber
24

Nah, seperti yang telah ditunjukkan Trufa, pada dasarnya ada dua cara untuk mengganti elemen tupel pada indeks tertentu. Ubah tupel menjadi daftar, ganti elemen dan ubah kembali, atau buat tupel baru dengan penggabungan.

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

Jadi, metode mana yang lebih baik, yaitu lebih cepat?

Ternyata untuk tupel pendek (pada Python 3.3), penggabungan sebenarnya lebih cepat!

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

Namun jika kita melihat tupel yang lebih panjang, konversi daftar adalah cara yang harus dilakukan:

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

Untuk tupel yang sangat panjang, konversi daftar jauh lebih baik!

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

Selain itu, kinerja metode penggabungan bergantung pada indeks tempat kita mengganti elemen. Untuk metode daftar, indeks tidak relevan.

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

Jadi: Jika tupel Anda pendek, potong dan gabungkan. Jika sudah lama, lakukan konversi daftar!

sjakobi
sumber
1
@Erikesty tidak selalu. Setelah kasus yang berguna adalah memperluas kelas yang tidak dapat Anda ubah dan yang metodenya mengembalikan tupel yang ingin Anda ubah hanya elemen pertama. return (val,) + res [1:] lebih jelas dari res2 = list (res); res2 [0] = val; return tuple (res2)
yucer
9

Ini dimungkinkan dengan satu liner:

values = ('275', '54000', '0.0', '5000.0', '0.0')
values = ('300', *values[1:])
Brian Spiering
sumber
1
Bagaimana Anda mengubah hanya elemen ketiga valuesdengan ini?
sdbbs
2
Inilah cara Anda dapat mengubah elemen apa pun dalam tupeli = 2; values = (*values[:i], '300', *values[i+1:])
Brian Spiering
8

Bukan berarti ini lebih unggul, tetapi jika ada yang penasaran dapat dilakukan dalam satu baris dengan:

tuple = tuple([200 if i == 0 else _ for i, _ in enumerate(tuple)])
bphi
sumber
Apakah itu lebih cepat dari tuple = tuple(200 if i == 0 else _ for i, _ in enumerate(tuple))? (Mengapa tidak pemahaman generator?)
Anonim
8

Saya yakin ini secara teknis menjawab pertanyaan, tetapi jangan lakukan ini di rumah. Saat ini, semua jawaban melibatkan pembuatan tupel baru, tetapi Anda dapat menggunakannya ctypesuntuk memodifikasi tupel dalam memori. Mengandalkan berbagai detail implementasi CPython pada sistem 64-bit, salah satu cara untuk melakukannya adalah sebagai berikut:

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
sumber
1
Selalu senang mengetahui sesuatu yang berbeda dari jawaban biasanya. Bersulang !
Kartheek Palepu
Orang yang ingin membuat kode dalam C mungkin harus melakukan hal itu. Meretas penerjemah seperti itu hanya melewatkan topik di sini. Ini juga tidak dapat diandalkan, karena detail implementasi cPython dapat berubah kapan saja tanpa peringatan dan kemungkinan akan merusak kode apa pun yang bergantung pada tupel yang menjadi imutable. Lebih jauh, tuple adalah objek koleksi paling ringan di python, jadi tidak ada masalah dengan membuat yang baru. Jika Anda benar-benar perlu sering memodifikasi koleksi, gunakan daftar sebagai gantinya.
Bachsau
1
Anda lupa mengurangi jumlah referensi ke nilai yang Anda buang. Itu akan menyebabkan kebocoran.
wizzwizz4
6

Seperti yang ditulis Hunter McMillen di komentar, tupel tidak dapat diubah, Anda perlu membuat tupel baru untuk mencapai ini. Misalnya:

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

EDIT: Ini tidak berfungsi pada tupel dengan entri duplikat !!

Berdasarkan ide Pooya :

Jika Anda berencana untuk sering melakukan ini (yang seharusnya tidak Anda lakukan karena tuple tidak dapat diubah karena suatu alasan), Anda harus melakukan sesuatu seperti ini:

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

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

Atau berdasarkan ide Jon :

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

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

Frist, tanyakan pada diri sendiri mengapa Anda ingin memutasi file tuple. Ada alasan mengapa string dan tuple tidak dapat diubah di Ptyhon , jika Anda ingin bermutasi tuplemaka itu mungkin listsaja.

Kedua, jika Anda masih ingin mengubah tupel Anda, maka Anda dapat mengubahnya tuplemenjadi a listlalu mengubahnya kembali, dan menetapkan kembali tupel baru ke variabel yang sama. Ini bagus jika Anda hanya akan memutasi tupel Anda sekali . Kalau tidak, menurut saya itu berlawanan dengan intuisi. Karena ini pada dasarnya membuat tupel baru dan setiap kali jika Anda ingin memutasi tupel tersebut, Anda harus melakukan konversi. Juga Jika Anda membaca kode, akan membingungkan untuk berpikir mengapa tidak membuat list? Tapi itu bagus karena tidak memerlukan perpustakaan apa pun.

Saya sarankan menggunakan mutabletuple(typename, field_names, default=MtNoDefault)dari mutabletuple 0.2 . Menurut saya pribadi cara ini lebih intuitif dan mudah dibaca. Pembacaan pribadi kode akan mengetahui bahwa penulis bermaksud untuk mengubah tupel ini di masa mendatang. Kelemahan dibandingkan dengan listmetode konversi di atas adalah Anda harus mengimpor file py tambahan.

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 : Jangan mencoba bermutasi tuple. jika Anda melakukannya dan itu adalah operasi satu kali ubah tupleke daftar, mutasikan, ubah listmenjadi baru tuple, dan tetapkan kembali ke variabel yang menahan lama tuple. Jika keinginan tupledan entah bagaimana ingin menghindari listdan ingin bermutasi lebih dari satu kali maka ciptakan mutabletuple.

OLIVER.KOO
sumber
2

berdasarkan Ide Jon dan Trufa tersayang

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

itu mengubah semua kejadian nilai lama Anda

Pooya
sumber
Ini akan sangat tidak nyaman (karena kurangnya kata yang lebih baik) jika Anda mengubah beberapa nilai tetapi sekali lagi, mengapa Anda ingin memodifikasi tupel di tempat pertama ...
Trufa
@Trufa ya, saya mencoba menulisnya: D
Pooya
Nama metode modify_tuple_by_index tidak akurat dan pasti menyebabkan kebingungan.
msw
1

Tidak boleh. Jika Anda ingin mengubahnya, Anda perlu menggunakan daftar, bukan tupel.

Perhatikan bahwa Anda bisa membuat tupel baru yang memiliki nilai baru sebagai elemen pertamanya.

BrenBarn
sumber
0

Saya telah menemukan cara terbaik untuk mengedit tupel adalah dengan membuat ulang tupel menggunakan versi sebelumnya sebagai basis.

Berikut adalah contoh yang saya gunakan untuk membuat versi warna yang lebih terang (saya sudah membukanya saat itu):

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

Apa yang dilakukannya, apakah itu melewati 'warna' tupel dan membaca setiap item, melakukan sesuatu padanya, dan akhirnya menambahkannya ke tupel baru.

Jadi yang Anda inginkan adalah seperti ini:

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

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

Yang spesifik itu tidak berfungsi, tetapi konsepnya adalah yang Anda butuhkan.

tuple = (0, 1, 2)

tuple = iterasi melalui tuple, ubah setiap item sesuai kebutuhan

itulah konsepnya.

Aedus
sumber
0

Saya terlambat ke permainan tetapi menurut saya cara yang paling sederhana , ramah sumber daya dan tercepat (tergantung pada situasinya) , adalah menimpa tupel itu sendiri. Karena ini akan menghilangkan kebutuhan untuk pembuatan daftar & variabel dan diarsipkan dalam satu baris.

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

>>> (1, 2, 24)

Tetapi: Ini hanya berguna untuk tupel yang agak kecil dan juga membatasi Anda pada nilai tupel tetap, bagaimanapun, ini adalah kasus untuk tupel sebagian besar waktu.

Jadi dalam kasus khusus ini akan terlihat seperti ini:

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
sumber
ini hanya berfungsi jika ini adalah tupel statis dengan panjang yang diketahui. bukan kode kemungkinan besar akan gagal cepat atau lambat karena terlalu spesifik ...
patroqueeet
ya - @patroqueeet, Oleh karena itu saya dengan jelas menyatakan kelemahan dari pendekatan ini, setelah But: ...-. Harap pertimbangkan kembali
suara negatif
1
hei, pertimbangkan kembali dan klik tombol. tetapi pemungutan suara sekarang dikunci oleh SO: /
patroqueeet
0

tldr; "solusi" adalah membuat objek tupel baru, tidak benar-benar memodifikasi aslinya

Meskipun ini adalah pertanyaan yang sangat lama, seseorang memberi tahu saya tentang kegilaan tupel bermutasi Python ini. Yang mana saya sangat terkejut / tertarik, dan melakukan googling, saya mendarat di sini (dan sampel serupa lainnya secara online)

Saya menjalankan beberapa tes untuk membuktikan teori saya

Catatan ==tidak menghargai persamaan sementara isapakah persamaan referensial (adalah objek yang sama dengan 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))

Hasil:

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

Anda tidak dapat mengubah item dalam tupel, tetapi Anda dapat mengubah properti objek yang dapat berubah dalam tupel (misalnya jika objek tersebut adalah daftar atau objek kelas yang sebenarnya)

Sebagai contoh

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')
unit G
sumber
-2

saya melakukan ini:

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

dan untuk berubah, lakukan saja

list[0]=6

dan u dapat mengubah tupel: D

ini dia disalin persis dari 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
sumber
2
tupel adalah daftar, bukan tupel. x = (y)tidak melakukan apa pun selain menugaskan y ke x.
m4tx
2
juga dengan menggunakan nama "tuple" dan "list" sebagai variabel, Anda akan kesulitan untuk membandingkan tuple dan jenis daftar nanti.
Michael Scott Cuthbert
-4

Anda dapat mengubah nilai tupel menggunakan salinan dengan referensi

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

>>> tuple2=tuple1

>>> tuple2
    [20, 30, 40]

>>> tuple2[1]=10

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

>>> print(tuple1)
    [20, 10, 40]
Rohit
sumber
1
Hanya ini daftar, bukan tupel, yang tetap dapat Anda ubah, diberi referensi kedua atau tidak.
Bachsau