Neden Python'da (veya herhangi bir değiştirilemez veri türünde) tupllere ihtiyacımız var?


140

Birkaç python öğretici (bir tanesi için Python Dive) ve Python.org dil referansı okudum - Dilin neden tuples ihtiyacı olduğunu anlamıyorum.

Tuples'ın bir liste veya kümeye kıyasla hiçbir yöntemi yoktur ve bunları sıralamak için bir tuple set veya listeye dönüştürmem gerekirse, ilk etapta bir tuple kullanmanın anlamı nedir?

Değişmezlik?

Neden bir değişkenin bellekte tahsis edildiğinden farklı bir yerde yaşayıp yaşamadığını önemsiyor? Python'daki bu değişmezlik işi fazlasıyla vurgulanmış görünüyor.

C / C ++ 'da bir işaretçi ayırır ve geçerli bir belleğe işaret edersem, kullanmadan önce adresin boş olmadığı sürece adresin nerede olduğu umurumda değildir.

Bu değişkene ne zaman başvursam, işaretçinin hala orijinal adrese işaret edip etmediğini bilmeme gerek yok. Sadece null olup olmadığını kontrol edip kullanıyorum (ya da kullanmıyorum).

Python'da, bir dize (veya tuple) ayırdığımda x'e atadığımda, dizeyi değiştirdiğimde, neden orijinal nesne olup olmadığına bakarım? Değişken verilerimi işaret ettiği sürece önemli olan budur.

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

x yine de istediğim verilere referansta bulunuyor, neden kimliğinin aynı mı yoksa farklı mı olduğuna kimin dikkat etmesi gerekiyor?


12
değişebilirliğin yanlış yönüne dikkat ediyorsunuz: "id aynı mı yoksa farklı mı" sadece bir yan etkidir; "Daha önce aynı nesneyi işaret eden diğer referansların işaret ettiği verilerin güncellemeleri yansıtıp yansıtmadığı" kritik öneme sahiptir.
Charles Duffy

Yanıtlar:


124
  1. değişmez nesneler önemli ölçüde optimizasyon sağlayabilir; Bu muhtemelen dizelerde Java'da değişmezdir, oldukça ayrı ancak Python ile aynı zamanda gelişmiştir ve hemen hemen her şey gerçekten işlevsel dillerde değişmezdir.

  2. özellikle Python'da sadece değişmezler yıkanabilir (ve dolayısıyla sözlüklerdeki setlerin veya anahtarların üyeleri). Yine, bu optimizasyon sağlar, ancak sadece "önemli" olmaktan çok daha fazlasıdır (tamamen değiştirilebilir nesneleri depolayan iyi karma tablolar tasarlamak bir kabustur - ya hash alır almaz her şeyin kopyalarını alırsınız ya da nesnenin hashını kontrol etmenin kabusu olur. son başvurudan bu yana değişti, çirkin kafasını taşıyor).

Optimizasyon sorununa örnek:

$ 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

11
@musicfreak, bir demet oluşturmanın eşdeğer listeyi oluşturmaktan 7,6 kat daha hızlı olduğu yerde yaptığım düzenlemeye bakın. Artık "farkedilir" " gerçekten tuhaf ...
Alex Martelli

11
@musicfreak Sanırım "erken optimizasyon tüm kötülüklerin köküdür". Bir uygulamada erken optimizasyon yapmak (örneğin, "tuples listelerden daha hızlı demek, bu yüzden tüm uygulamada sadece tuples kullanacağız!") Ve karşılaştırmalar yapmak arasında büyük bir fark var. Alex'in kıyaslaması anlayışlı ve bir liste oluşturmanın bir liste oluşturmaktan daha hızlı olduğunu bilmek, gelecekteki optimizasyon işlemlerinde (gerçekten gerekli olduğunda) bize yardımcı olabilir.
Virgil Dupras

5
@Alex, "bina" bir liste oluşturmak "liste oluşturmaktan" çok daha hızlı mı yoksa Python çalışma zamanının bu diziyi önbelleğe almasının sonucunu mu görüyoruz? Bana ikinci gibi geliyor.
Triptik

6
@ACoolie, bu randomçağrılar tamamen hakim (sadece bunu deneyin, göreceksiniz!), Bu yüzden çok önemli değil. Deneyin python -mtimeit -s "x=23" "[x,x]"ve liste oluşturmak için tuple vs 2-3 kez daha anlamlı bir hızlanma göreceksiniz.
Alex Martelli

9
merak eden herkes için - listelerden tuplelara geçerek bir saatten fazla veri işlemeyi kesebildik.
Mark Ribau

42

Yukarıdaki cevapların hiçbiri, Python'a yeni gelen pek çok şeyin tam olarak anlamadığı görünen tuples ve listelerin gerçek sorununa işaret etmiyor.

Tuples ve listeler farklı amaçlara hizmet eder. Homojen verileri depolar. Bunun gibi bir listeye sahip olabilirsiniz ve olması gerekir:

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

Listelerin doğru kullanılmasının nedeni, bunların hepsinin homojen veri türleri, özellikle de insanların adları olmasıdır. Ancak şöyle bir liste alın:

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

Bu liste bir kişinin tam adı ve yaşıdır. Bu bir veri türü değil. Bu bilgileri depolamanın doğru yolu bir demet içinde ya da bir nesnedir. Diyelim ki birkaç tane var:

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

Tuples ve Listelerin değişmezliği ve değişebilirliği temel fark değildir. Liste, aynı tür öğelerin listesidir: dosyalar, adlar, nesneler. Tuples, farklı nesne türlerinin bir gruplandırmasıdır. Farklı kullanımları vardır ve birçok Python kodlayıcısı, tuple'lerin ne anlama geldiğini gösteren listeleri kötüye kullanır.

Lütfen yapma.


Düzenle:

Sanırım bu blog yazısı bunu neden benden daha iyi düşündüğümü açıklıyor: http://news.e-scribe.com/397


13
En azından benim tarafımdan kabul edilmeyen bir vizyonunuz olduğunu düşünüyorum, diğerlerini tanımıyorum.
Stefano Borini

13
Bu yanıta da kesinlikle katılmıyorum. Verilerin homojenliğinin, bir liste veya bir demet kullanmanızla kesinlikle bir ilgisi yoktur. Python'daki hiçbir şey bu ayrımı önermez.
Glenn Maynard

14
Guido bu noktayı birkaç yıl önce de dile getirdi. aspn.activestate.com/ASPN/Mail/Message/python-list/1566320
John La Rooy

11
Her ne kadar Guido (Python'un tasarımcısı) homojen veriler için kullanılacak listeler ve heterojen olanlar için tupler tasarlamasına rağmen, gerçek şu ki, dil bunu zorlamıyor. Bu nedenle, bu yorumun her şeyden çok bir stil meselesi olduğunu düşünüyorum. Birçok insanın tipik kullanım durumlarında, listeler dizi benzeri ve tizler rekor benzeri olma eğilimindedir. Ancak bu, sorunlarına daha iyi uyuyorsa insanların heterojen veriler için listeler kullanmasını engellememelidir. Python Zen'in dediği gibi: Pratiklik saflığı yener.
John Y

9
@ Glenn, temelde yanılıyorsun. Tuple'ların başlıca kullanımlarından biri, ilişkili birden fazla veri parçasını saklamak için bileşik bir veri türüdür. Bir demet üzerinde yineleyebilmeniz ve aynı işlemlerin çoğunu yapabilmeniz bunu değiştirmez. (Referans olarak, diğer birçok dilde tuple'lerin liste meslektaşları ile aynı yinelenebilir özelliklere sahip olmadığını düşünün)
HS.

22

bir diziyi sıralayabilmek için bir kümeye veya listeye dönüştürmem gerekiyorsa, öncelikle bir diziyi kullanmanın anlamı nedir?

Bu özel durumda, muhtemelen bir anlamı yoktur. Bu bir sorun değil, çünkü bu bir demet kullanmayı düşüneceğiniz durumlardan biri değil.

İşaret ettiğiniz gibi, tuples değişmez. Değişmez tiplere sahip olmanın nedenleri tuple için geçerlidir:

  • kopyalama verimliliği: değişmez bir nesneyi kopyalamak yerine onu diğer adla değiştirebilirsiniz (bir değişkeni bir referansa bağlayabilirsiniz)
  • karşılaştırma verimliliği: referansla kopyalamayı kullanırken, içeriği değil konumu karşılaştırarak iki değişkeni karşılaştırabilirsiniz
  • stajyerlik: herhangi bir değişmez değerin en fazla bir kopyasını saklamanız gerekir
  • eşzamanlı kodda değiştirilemeyen nesnelere erişimi senkronize etmeye gerek yoktur
  • const doğruluk: bazı değerlerin değiştirilmesine izin verilmemelidir. Bu (benim için) değişmez türlerin ana nedenidir.

Belirli bir Python uygulamasının yukarıdaki özelliklerin tümünü kullanamayabileceğini unutmayın.

Sözlük anahtarları değiştirilemez olmalıdır, aksi takdirde bir anahtar nesnenin özelliklerini değiştirmek temeldeki veri yapısının değişmezlerini geçersiz kılabilir. Böylece tüller potansiyel olarak anahtar olarak kullanılabilir. Bu, const doğruluğunun bir sonucudur.

Ayrıca "Bkz Tanıtımı dizilerini gelen," Dalış içine Python .


2
id ((1,2,3)) == id ((1,2,3)) yanlış. Tuple'ları yalnızca konumu karşılaştırarak karşılaştıramazsınız, çünkü referans olarak kopyalandıklarının garantisi yoktur.
Glenn Maynard

@Glenn: "Referansa göre kopyalama kullandığınızda" nitel notuna dikkat edin. Kodlayıcı kendi uygulamasını oluşturabilse de, tuples için referans kopya, büyük ölçüde tercüman / derleyici için bir konudur. Çoğunlukla ==platform düzeyinde nasıl uygulandığından bahsediyordum .
outis

1
@Glenn: ayrıca referansla kopyalamanın içindeki tuples için geçerli olmadığını unutmayın (1,2,3) == (1,2,3). Bu daha çok bir staj konusu.
outis

Açıkça söylediğim gibi, referans olarak kopyalandıklarına dair bir garanti yoktur . Tlesles Python'da stajyer değil; Bu bir dize konsepti.
Glenn Maynard

Çok açık bir şekilde söylediğim gibi: Programcıdan tuples'i konumu karşılaştırarak karşılaştırmıyorum. Platformun, referansla kopyalamayı garanti edebilecek olan olasılığından bahsediyorum. Ayrıca, staj, sadece tellere değil, herhangi bir değişmez tipe uygulanabilir. Ana Python uygulaması stajyer değişmez tipler olmayabilir, ancak Python'un değişmez tiplere sahip olması interneti bir seçenek haline getirir.
outis

15

Bazen nesneleri sözlük anahtarı olarak kullanmak isteriz

Değeri için, son zamanlarda tuples (2.6+) büyüdü index()ve count()yöntemler


5
+1: Sözlük anahtarı olarak değiştirilebilir bir liste (veya değiştirilebilir set veya değiştirilebilir sözlük) çalışmaz. Bu yüzden değişmez listelere ("tuples"), dondurulmuş setlere ve ... iyi ... dondurulmuş bir sözlüğe ihtiyacımız var.
S.Lott

9

Her zaman aynı temel veri yapısının (diziler) garip bir tasarım olması için iki ayrı tipe sahip olduğunu gördüm, ancak pratikte gerçek bir sorun değil. (Her dilin siğilleri vardır, Python dahil, ancak bu önemli değildir.)

Neden bir değişkenin bellekte tahsis edildiğinden farklı bir yerde yaşayıp yaşamadığını önemsiyor? Python'daki bu değişmezlik işi fazlasıyla vurgulanmış görünüyor.

Bunlar farklı şeyler. Değişebilirlik, bellekte depolandığı yerle ilgili değildir; işaret edeceği şeylerin değişemeyeceği anlamına gelir .

Python nesneleri oluşturulduktan, değiştirildikten veya değiştirilmedikten sonra konumu değiştiremez. (Daha doğrusu, id () değeri değişemez - pratikte aynı şey.) Değişebilir nesnelerin dahili depolaması değişebilir, ancak bu gizli bir uygulama detayıdır.

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

Bu, değişkeni değiştirmez ("değiştirir"); aynı ada sahip yeni bir değişken oluşturur ve eskisini atar. Bir mutasyon işlemiyle karşılaştırın:

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

Diğerlerinin de belirttiği gibi, dizilerin sözlükler ve değişmezlik gerektiren diğer veri yapıları için anahtar olarak kullanılmasına izin verir.

Sözlük anahtarlarının tamamen değişmez olması gerekmediğini unutmayın. Sadece anahtar olarak kullanılan kısmının değişmez olması gerekir; bazı kullanımlar için bu önemli bir ayrımdır. Örneğin, bir kullanıcıyı temsil eden, eşitlik ve karma değerini benzersiz kullanıcı adıyla karşılaştıran bir sınıfınız olabilir. Daha sonra sınıfta diğer değişken verileri asabilirsiniz - "kullanıcı giriş yaptı", vb. Bu eşitlik veya karmayı etkilemediğinden, bunu sözlükte bir anahtar olarak kullanmak mümkündür ve mükemmel bir şekilde geçerlidir. Bu Python'da çok yaygın olarak gerekli değildir; Ben sadece birkaç kişi anahtarların "değişmez" olması gerektiğini iddia etti, çünkü bu sadece kısmen doğru işaret. Bunu birçok kez C ++ haritaları ve setleriyle birlikte kullandım.


>>> a = [1,2,3] >>> id (a) 3084599212L >>> a [1] = 5 >>> a [1, 5, 3] >>> id (a) 3084599212L Sen ' Değişebilir bir veri türünü değiştirdim, bu yüzden orijinal soru ile anlam ifade etmiyor. x = 'merhaba "id (x) 12345 x =" güle güle "id (x) 65432 Yeni bir nesne olup olmadığına kim önem veriyor. x, atadığım verilere işaret ettiği sürece önemli olan bu.
pyNewGuy

4
Size yardım etme yeteneğimin ötesinde kafan karıştı.
Glenn Maynard

Tuple'lerin değerini algılamada ana zorluk kaynağı gibi görünen alt sorulardaki karışıklığı işaret etmek için +1.
outis

1
Yapabilseydim, anahtarlar için gerçek değerlendirme tablosunun nesnenin yıkanabilir olup olmadığına işaret etmek için başka bir +1 ( docs.python.org/glossary.html#term-hashable ).
outis

7

Bir yorumda sunulan gnibbler'ın Guido'nun tamamen kabul edilmeyen / takdir edilmeyen bir görüşü vardı : “listeler homojen veriler için, tupeller heterojen veriler içindir”. Elbette, muhaliflerin çoğu bunu bir listenin tüm öğelerinin aynı türden olması gerektiği şeklinde yorumladı.

Farklı görmeyi seviyorum , geçmişte başkalarının aksine değil :

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

Tip (alist [1])! = Tip (alist [2]) olsa bile alistin homojen olduğunu düşünüyorum.

Elemanların sırasını değiştirebilir ve kodumda (varsayımlar dışında, örneğin "sıralanmalıdır") sorunum olmazsa, bir liste kullanılmalıdır. Değilse ( blueyukarıdaki demette olduğu gibi), o zaman bir demet kullanmalıyım.


Yapabilseydim bu cevabı 15 kere oy verirdim. Tuples hakkında tam da böyle hissediyorum.
Grant Paul

6

Arayan kişiye geçtikleri nesnenin mutasyona uğramayacağını garanti ettikleri için önemlidirler. Eğer bunu yaparsan:

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

Arayanın, aramadan sonraki a değerinin garantisi yoktur . Ancak,

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

Şimdi arayan veya bu kodun bir okuyucu olarak biliyoruz bir aynıdır. Bu senaryo için her zaman listenin bir kopyasını oluşturabilir ve iletebilirsiniz, ancak şimdi daha anlamsal mantıklı bir dil yapısı kullanmak yerine döngüleri boşa harcıyorsunuz.


1
Bu, tupleslerin çok ikincil bir özelliğidir. Önceden varolan bir liste veya başka bir sınıf olsun, bir işleve geçmek istediğiniz ve değiştirilemeyen değiştirilebilir bir nesneye sahip olduğunuz çok fazla durum vardır. Python'da sadece "başvuru ile const parametreleri" kavramı yoktur (örn. Const foo & C ++ 'da). Tuplelar size bir tuple kullanmanın uygun olması durumunda bunu verir, ancak arayandan bir liste aldıysanız, başka bir yere geçmeden önce gerçekten bir tuple'e dönüştürecek misiniz?
Glenn Maynard

Bu konuda sana katılıyorum. Bir grup, bir const anahtar sözcüğünü tokatlamakla aynı şey değildir. Demek istediğim, bir grubun değişmezliğinin, kodun okuyucusuna anlam katmasıdır. Her ikisinin de işe yarayacağı ve beklentinizin, tuple kullanarak değişmemesi gerektiği göz önüne alındığında, okuyucu için ekstra anlam katacaktır (bunu sağlarken)
Matthew Manela

a = [1,1,1] doWork (a) dowork (), def dowork (arg) olarak tanımlanırsa: arg = [0,0,0] bir listede veya grupta dowork () öğesini çağırmak aynı sonucu verir
pyNewGuy


1

Sorunuz (ve takip yorumları) bir ödev sırasında id () değerinin değişip değişmediğine odaklanır. Değişmez nesnenin değiştirilmesi ve değişebilir nesne modifikasyonu arasındaki farkın bu takip etkisine odaklanmak, farkın kendisinden ziyade belki de en iyi yaklaşım değildir.

Devam etmeden önce, aşağıda gösterilen davranışın Python'dan beklediğiniz şey olduğundan emin olun.

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

Bu durumda, sadece a1'in yeni bir değeri atanmış olmasına rağmen a2'nin içeriği değiştirildi. Aşağıdakilerin aksine:

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

Bu son durumda, içeriğini güncellemek yerine listenin tamamını değiştirdik. Tuples gibi değişmez tiplerde, izin verilen tek davranış budur.

Bu neden önemli? Diyelim ki bir kararınız var:

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

Bir demet kullanarak sözlük, anahtarlarının "altından" farklı bir değere hash edilen öğelere dönüştürülmesini güvenlidir. Bu, etkili uygulama için kritik öneme sahiptir.


Diğerlerinin de belirttiği gibi, değişmezlik! = Hashabilite. Tüm tuples sözlük anahtarı olarak kullanılamaz: {([1], [2]): 'value'} başarısız olur çünkü tupledaki değişken listeler değiştirilebilir, ancak {((1), (2)): ' '} değeri TAMAM.
Ned Deily

Ned, bu doğru, ama ayrımın sorulan soruya açık olduğundan emin değilim.
Charles Duffy

@ K.Nicholas, burada onayladığınız düzenleme, kodu bir demet değil, bir tamsayı atayacak şekilde değiştirdi - daha sonra dizin işlemlerinin başarısız olmasını sağladı, böylece yeni transkript aslında mümkün oldu. Doğru tanımlanmış sorun, elbette; geçersiz çözüm.
Charles Duffy

@MichaelPuckettII, aynı şekilde, yukarıya bakınız.
Charles Duffy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.