Mengapa kita perlu tupel dengan Python (atau tipe data yang tidak dapat diubah)?

140

Saya telah membaca beberapa tutorial python (Dive Into Python, misalnya), dan referensi bahasa di Python.org - Saya tidak mengerti mengapa bahasa tersebut membutuhkan tupel.

Tuples tidak memiliki metode dibandingkan dengan daftar atau set, dan jika saya harus mengonversi tuple ke set atau daftar untuk dapat mengurutkannya, apa gunanya menggunakan tuple?

Kekekalan?

Mengapa ada yang peduli jika variabel tinggal di tempat yang berbeda di memori daripada ketika itu awalnya dialokasikan? Seluruh bisnis keabadian dalam Python ini tampaknya terlalu ditekankan.

Dalam C / C ++ jika saya mengalokasikan pointer dan menunjuk ke beberapa memori yang valid, saya tidak peduli di mana alamat itu berada selama itu bukan nol sebelum saya menggunakannya.

Setiap kali saya referensi variabel itu, saya tidak perlu tahu apakah pointer masih menunjuk ke alamat asli atau tidak. Saya hanya memeriksa null dan menggunakannya (atau tidak).

Dalam Python, ketika saya mengalokasikan string (atau tuple) menetapkannya ke x, lalu memodifikasi string, mengapa saya peduli jika itu objek asli? Selama variabel menunjuk ke data saya, itu yang terpenting.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x masih referensi data yang saya inginkan, mengapa ada yang perlu peduli jika id-nya sama atau berbeda?

pyNewGuy
sumber
12
Anda memperhatikan aspek salah yang dapat berubah-ubah: "apakah idnya sama atau berbeda" hanyalah efek samping; "apakah data diarahkan oleh referensi lain yang sebelumnya menunjuk ke objek yang sama sekarang mencerminkan pembaruan" sangat penting.
Charles Duffy

Jawaban:

124
  1. objek yang tidak berubah dapat memungkinkan pengoptimalan yang substansial; ini mungkin mengapa string juga tidak dapat diubah di Jawa, dikembangkan cukup terpisah tetapi sekitar waktu yang sama dengan Python, dan hampir semuanya tidak berubah dalam bahasa yang benar-benar fungsional.

  2. khususnya dalam Python, hanya yang tidak berubah yang dapat hashable (dan, oleh karena itu, anggota set, atau kunci dalam kamus). Sekali lagi, ini mampu optimasi, tetapi jauh lebih dari sekedar "substansial" (mendesain tabel hash yang layak menyimpan objek yang benar-benar bisa berubah adalah mimpi buruk - baik Anda mengambil salinan dari segala sesuatu segera setelah Anda hash, atau mimpi buruk memeriksa apakah hash objek tersebut telah berubah sejak Anda terakhir kali mengambil referensi untuk itu kepala jelek nya).

Contoh masalah pengoptimalan:

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop
Alex Martelli
sumber
11
@musicfreak, lihat hasil edit yang baru saja saya lakukan di mana membangun tuple lebih dari 7,6 kali lebih cepat daripada membangun daftar yang setara - sekarang Anda tidak bisa mengatakan Anda sudah "tidak pernah melihat perbedaan yang nyata" lagi, kecuali definisi Anda tentang "terlihat" " Benar - benar aneh ...
Alex Martelli
11
@musicfreak Saya pikir Anda menyalahgunakan "optimasi prematur adalah akar dari semua kejahatan". Ada perbedaan besar antara melakukan pengoptimalan prematur dalam suatu aplikasi (misalnya, mengatakan "tuple lebih cepat daripada daftar, jadi kita hanya akan menggunakan tupel di semua aplikasi!") Dan melakukan tolok ukur. Benchmark Alex sangat mendalam dan mengetahui bahwa membangun tuple lebih cepat daripada membangun daftar mungkin membantu kami dalam operasi pengoptimalan di masa mendatang (saat itu benar-benar diperlukan).
Duplica Virgil
5
@Alex, apakah "membangun" tuple benar-benar lebih cepat daripada "membangun daftar", atau apakah kita melihat hasil runtime Python yang menyimpan cache tuple? Sepertinya yang terakhir bagi saya.
Triptych
6
@ Acoolie, itu benar-benar didominasi oleh randompanggilan (coba lakukan saja, Anda akan lihat!), Jadi tidak terlalu signifikan. Coba python -mtimeit -s "x=23" "[x,x]"dan Anda akan melihat speedup lebih bermakna 2-3 kali untuk membangun tuple vs membangun daftar.
Alex Martelli
9
untuk siapa pun yang bertanya-tanya - kami dapat mengurangi lebih dari satu jam pemrosesan data dengan beralih dari daftar ke tupel.
Mark Ribau
42

Tak satu pun dari jawaban di atas menunjukkan masalah sebenarnya dari daftar tuple vs, yang banyak orang baru di Python tampaknya tidak sepenuhnya mengerti.

Tuples dan daftar melayani tujuan yang berbeda. Daftar menyimpan data yang homogen. Anda dapat dan harus memiliki daftar seperti ini:

["Bob", "Joe", "John", "Sam"]

Alasan penggunaan daftar yang benar adalah karena semua itu adalah tipe data yang homogen, khususnya, nama orang. Tetapi ambillah daftar seperti ini:

["Billy", "Bob", "Joe", 42]

Daftar itu adalah nama lengkap satu orang, dan umur mereka. Itu bukan satu jenis data. Cara yang benar untuk menyimpan informasi itu adalah dalam tuple, atau dalam suatu objek. Katakanlah kita memiliki beberapa:

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

Kekekalan dan ketidakstabilan Tuples and Lists bukanlah perbedaan utama. Daftar adalah daftar jenis barang yang sama: file, nama, objek. Tuples adalah pengelompokan dari berbagai jenis objek. Mereka memiliki kegunaan yang berbeda, dan banyak daftar penyalahgunaan kode Python untuk apa tuple dimaksudkan untuk.

Tolong jangan.


Edit:

Saya pikir posting blog ini menjelaskan mengapa saya pikir ini lebih baik daripada yang saya lakukan: http://news.e-scribe.com/397

Hibah Paul
sumber
13
Saya pikir Anda memiliki visi yang setidaknya tidak disetujui oleh saya, tidak tahu yang lain.
Stefano Borini
13
Saya juga sangat tidak setuju dengan jawaban ini. Homogenitas data sama sekali tidak ada hubungannya dengan apakah Anda harus menggunakan daftar atau tupel. Tidak ada dalam Python yang menyarankan perbedaan ini.
Glenn Maynard
14
Guido membuat poin ini beberapa tahun yang lalu juga. aspn.activestate.com/ASPN/Mail/Message/python-list/1566320
John La Rooy
11
Meskipun Guido (perancang Python) bermaksud agar daftar digunakan untuk data yang homogen dan tupel untuk heterogen, faktanya adalah bahasa tersebut tidak memberlakukan ini. Oleh karena itu, saya pikir interpretasi ini lebih merupakan masalah gaya daripada hal lainnya. Kebetulan dalam banyak kasus penggunaan umum, daftar cenderung seperti array dan tuple cenderung seperti catatan. Tapi ini seharusnya tidak menghentikan orang dari menggunakan daftar untuk data heterogen jika lebih cocok dengan masalah mereka. Seperti yang dikatakan Zen dari Python: Kepraktisan mengalahkan kemurnian.
John Y
9
@ Glenn, Anda pada dasarnya salah. Salah satu penggunaan utama tupel adalah sebagai tipe data komposit untuk menyimpan beberapa bagian data yang terkait. Fakta bahwa Anda dapat beralih pada tuple dan melakukan banyak operasi yang sama tidak mengubah ini. (Sebagai referensi pertimbangkan bahwa tupel dalam banyak bahasa lain tidak memiliki fitur yang sama seperti rekan-rekan daftar mereka)
HS.
22

jika saya harus mengonversi tuple ke set atau daftar untuk dapat mengurutkannya, apa gunanya menggunakan tuple?

Dalam kasus khusus ini, mungkin tidak ada benarnya. Ini bukan masalah, karena ini bukan salah satu kasus di mana Anda akan mempertimbangkan untuk menggunakan tuple.

Seperti yang Anda tunjukkan, tuple tidak berubah. Alasan memiliki jenis yang tidak dapat diubah berlaku untuk tupel:

  • efisiensi penyalinan: alih-alih menyalin objek yang tidak dapat diubah, Anda dapat alias itu (mengikat variabel ke referensi)
  • efisiensi perbandingan: ketika Anda menggunakan copy-by-reference, Anda dapat membandingkan dua variabel dengan membandingkan lokasi, bukan konten
  • magang: Anda perlu menyimpan paling banyak satu salinan dari nilai yang tidak berubah
  • tidak perlu menyinkronkan akses ke objek yang tidak dapat diubah dalam kode bersamaan
  • benar const: beberapa nilai tidak boleh dibiarkan berubah. Ini (bagi saya) adalah alasan utama untuk tipe yang tidak dapat diubah.

Perhatikan bahwa implementasi Python tertentu mungkin tidak menggunakan semua fitur di atas.

Kunci kamus harus tidak berubah, jika tidak mengubah properti objek-kunci dapat membatalkan invarian dari struktur data yang mendasarinya. Tuples dengan demikian berpotensi digunakan sebagai kunci. Ini adalah konsekuensi dari kebenaran const.

Lihat juga " Memperkenalkan tupel ", dari Dive Into Python .

outis
sumber
2
id ((1,2,3)) == id ((1,2,3)) salah. Anda tidak dapat membandingkan tupel hanya dengan membandingkan lokasi, karena tidak ada jaminan bahwa mereka disalin dengan referensi.
Glenn Maynard
@Glenn: Catat komentar yang memenuhi syarat "ketika Anda menggunakan copy-by-reference". Sementara coder dapat membuat implementasi sendiri, copy-by-reference untuk tuple sebagian besar merupakan masalah bagi interpreter / compiler. Saya kebanyakan merujuk pada bagaimana ==diimplementasikan di tingkat platform.
outis
1
@Glenn: juga mencatat bahwa referensi copy-by-tidak berlaku untuk tupel di (1,2,3) == (1,2,3). Itu lebih merupakan masalah magang.
outis
Seperti saya katakan dengan cukup jelas, tidak ada jaminan bahwa mereka disalin dengan referensi . Tuples tidak diinternir dengan Python; itu konsep string.
Glenn Maynard
Seperti saya katakan dengan sangat jelas: Saya tidak berbicara tentang programmer membandingkan tuple dengan membandingkan lokasi. Saya sedang berbicara tentang kemungkinan bahwa platform dapat, yang dapat menjamin salinan-oleh-referensi. Selain itu, pemagangan dapat diterapkan untuk semua jenis yang tidak dapat diubah, tidak hanya string. Implementasi Python utama mungkin tidak menginternir tipe yang tidak dapat diubah, tetapi fakta bahwa Python memiliki tipe yang tidak dapat diubah membuat menginternir opsi.
outis
15

Terkadang kita suka menggunakan objek sebagai kunci kamus

Untuk apa nilainya, tuple baru-baru ini (2,6+) tumbuh index()dan count()metode

John La Rooy
sumber
5
+1: Daftar yang bisa diubah (atau kamus yang bisa diubah atau berubah-ubah) sebagai kunci kamus tidak bisa berfungsi. Jadi kita perlu daftar yang tidak berubah ("tuple"), set beku, dan ... yah ... kamus beku, kurasa.
S.Lott
9

Saya selalu menemukan memiliki dua tipe yang benar-benar terpisah untuk struktur data dasar yang sama (array) menjadi desain yang canggung, tetapi bukan masalah nyata dalam praktiknya. (Setiap bahasa memiliki kutilnya, termasuk Python, tetapi ini bukan yang penting.)

Mengapa ada yang peduli jika variabel tinggal di tempat yang berbeda di memori daripada ketika itu awalnya dialokasikan? Seluruh bisnis keabadian dalam Python ini tampaknya terlalu ditekankan.

Ini adalah hal yang berbeda. Mutabilitas tidak terkait dengan tempat penyimpanannya; itu berarti hal - hal yang ditunjuknya tidak dapat diubah.

Objek python tidak dapat mengubah lokasi setelah mereka dibuat, bisa berubah atau tidak. (Lebih tepatnya, nilai id () tidak bisa berubah - hal yang sama, dalam praktiknya.) Penyimpanan internal objek yang bisa berubah bisa berubah, tapi itu detail implementasi tersembunyi.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

Ini tidak mengubah ("bermutasi") variabel; itu membuat variabel baru dengan nama yang sama, dan membuang yang lama. Bandingkan dengan operasi yang bermutasi:

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

Seperti yang telah ditunjukkan orang lain, ini memungkinkan penggunaan array sebagai kunci untuk kamus, dan struktur data lainnya yang membutuhkan ketetapan.

Perhatikan bahwa kunci kamus tidak harus sepenuhnya tidak dapat diubah. Hanya bagian dari itu yang digunakan sebagai kunci yang harus kekal; untuk beberapa kegunaan, ini adalah perbedaan penting. Misalnya, Anda bisa memiliki kelas yang mewakili pengguna, yang membandingkan kesetaraan dan hash dengan nama pengguna yang unik. Anda kemudian dapat menggantung data yang bisa berubah-ubah di kelas - "pengguna masuk", dll. Karena ini tidak memengaruhi kesetaraan atau hash, dimungkinkan dan sangat valid untuk menggunakan ini sebagai kunci dalam kamus. Ini tidak terlalu umum dibutuhkan dalam Python; Saya hanya menunjukkannya karena beberapa orang telah mengklaim bahwa kunci harus "tidak berubah", yang hanya sebagian benar. Saya telah menggunakan ini berkali-kali dengan C ++ map dan set, meskipun.

Glenn Maynard
sumber
>>> a = [1,2,3] >>> id (a) 3084599212L >>> a [1] = 5 >>> a [1, 5, 3] >>> id (a) 3084599212L You ' Saya baru saja memodifikasi tipe data yang bisa berubah, sehingga tidak masuk akal terkait dengan pertanyaan awal. x = 'hello "id (x) 12345 x =" selamat tinggal "id (x) 65432 Siapa yang peduli apakah itu objek baru atau tidak. Selama x menunjuk ke data yang telah saya tetapkan, hanya itu yang penting.
pyNewGuy
4
Anda bingung jauh di luar kemampuan saya untuk membantu Anda.
Glenn Maynard
+1 untuk menunjukkan kebingungan dalam sub-pertanyaan, yang tampaknya menjadi sumber utama kesulitan dalam memahami nilai tupel.
outis
1
Jika saya bisa, +1 lain yang menunjukkan bahwa rubrik sebenarnya untuk kunci adalah apakah objek tersebut dapat hashable ( docs.python.org/glossary.html#term-hashable ).
outis
7

Seperti yang ditawarkan gnibbler dalam komentar, Guido memiliki pendapat yang tidak sepenuhnya diterima / dihargai: "daftar adalah untuk data homogen, tuple adalah untuk data heterogen". Tentu saja, banyak penentang menafsirkan ini sebagai makna bahwa semua elemen daftar harus dari jenis yang sama.

Saya suka melihatnya secara berbeda, tidak seperti orang lain di masa lalu:

blue= 0, 0, 255
alist= ["red", "green", blue]

Perhatikan bahwa saya menganggap alist sebagai homogen, walaupun tipe (alist [1])! = Tipe (alist [2]).

Jika saya dapat mengubah urutan elemen dan saya tidak akan memiliki masalah dalam kode saya (terlepas dari asumsi, misalnya "itu harus diurutkan"), maka daftar harus digunakan. Jika tidak (seperti pada tuple di blueatas), maka saya harus menggunakan tuple.

tzot
sumber
Jika saya bisa, saya akan memilih jawaban ini hingga 15 kali. Ini persis seperti yang saya rasakan tentang tuple.
Berikan Paul
6

Mereka penting karena mereka menjamin penelepon bahwa objek yang mereka lewati tidak akan bermutasi. Jika kamu melakukan ini:

a = [1,1,1]
doWork(a)

Penelepon tidak memiliki jaminan dari nilai yang setelah panggilan. Namun,

a = (1,1,1)
doWorK(a)

Sekarang Anda sebagai penelepon atau sebagai pembaca kode ini tahu itu a adalah sama. Anda selalu dapat membuat skenario ini salinan daftar dan lulus itu tetapi sekarang Anda membuang-buang siklus daripada menggunakan konstruksi bahasa yang lebih masuk akal semantik.

Matthew Manela
sumber
1
Ini adalah properti yang sangat sekunder dari tuple. Ada terlalu banyak kasus di mana Anda memiliki objek yang bisa diubah, Anda ingin meneruskan ke fungsi dan tidak mengubahnya, apakah itu daftar yang sudah ada sebelumnya atau beberapa kelas lainnya. Tidak ada konsep "parameter const dengan referensi" di Python (mis. Const foo & di C ++). Tuples kebetulan memberi Anda ini jika kebetulan nyaman menggunakan tuple sama sekali, tetapi jika Anda telah menerima daftar dari penelepon Anda, apakah Anda benar-benar akan mengubahnya menjadi tuple sebelum meneruskannya di tempat lain?
Glenn Maynard
Saya setuju dengan Anda tentang hal itu. Sebuah tuple tidak sama dengan menampar kata kunci const. Maksud saya adalah bahwa kekekalan sebuah tuple membawa makna tambahan bagi pembaca kode. Diberikan situasi di mana keduanya akan bekerja dan harapan Anda adalah bahwa itu tidak boleh berubah menggunakan tuple akan menambah makna tambahan bagi pembaca (sambil memastikan itu juga)
Matthew Manela
a = [1,1,1] doWork (a) jika dowork () didefinisikan sebagai def dowork (arg): arg = [0,0,0] memanggil dowork () pada daftar atau tuple memiliki hasil yang sama
pyNewGuy
1

Anda dapat melihat di sini untuk beberapa diskusi tentang ini

ghostdog74
sumber
1

Pertanyaan Anda (dan komentar tindak lanjut) fokus pada apakah id () berubah selama tugas. Berfokus pada efek lanjutan dari perbedaan antara penggantian objek yang tidak berubah dan modifikasi objek yang dapat berubah daripada perbedaan itu sendiri mungkin bukan pendekatan terbaik.

Sebelum kita melanjutkan, pastikan perilaku yang ditunjukkan di bawah ini adalah yang Anda harapkan dari Python.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

Dalam hal ini, isi a2 diubah, meskipun hanya a1 yang memiliki nilai baru yang ditetapkan. Kontras dengan yang berikut:

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

Dalam kasus terakhir ini, kami mengganti seluruh daftar, daripada memperbarui kontennya. Dengan tipe yang tidak dapat diubah seperti tuple, ini adalah satu-satunya perilaku yang diizinkan.

Mengapa ini penting? Katakanlah Anda memiliki dict:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

Menggunakan tuple, kamus aman dari kuncinya diubah "keluar dari bawah" ke item yang hash ke nilai berbeda. Ini penting untuk memungkinkan implementasi yang efisien.

Charles Duffy
sumber
Seperti yang telah ditunjukkan orang lain, kekekalan! = Memiliki kemampuan Tidak semua tupel dapat digunakan sebagai kunci kamus: {([1], [2]): 'value'} gagal karena daftar yang dapat diubah dalam tupel dapat diubah, tetapi {((1), (2)): ' nilai '} tidak apa-apa.
Ned Deily
Ned, itu benar, tapi aku tidak yakin perbedaannya cocok dengan pertanyaan yang diajukan.
Charles Duffy
@ K.Nicholas, hasil edit yang Anda setujui di sini mengubah kode sedemikian rupa untuk menetapkan bilangan bulat, bukan tuple, sama sekali - membuat operasi indeks berikutnya gagal, sehingga mereka tidak mungkin menguji bahwa yang baru transkrip sebenarnya mungkin. Masalah yang diidentifikasi dengan benar, tentu saja; solusi yang tidak valid.
Charles Duffy
@MichaelPuckettII, juga, lihat di atas.
Charles Duffy