Python'da neden grup kavrayışı yok?


340

Hepimizin bildiği gibi, liste kavrayışı var,

[i for i in [1, 2, 3, 4]]

ve sözlük anlama var,

{i:j for i, j in {1: 'a', 2: 'b'}.items()}

fakat

(i for i in (1, 2, 3))

sonunda bir kavrayıcıya değil, bir jeneratöre dönüşecektir tuple. Neden?

Benim tahminim a tupledeğişmez, ama bu cevap gibi görünmüyor.


16
Ayrıca bir
dik

3
Kodunuzda bir Sözdizimi hatası var: {i:j for i,j in {1:'a', 2:'b'}}olması gereken{i:j for i,j in {1:'a', 2:'b'}.items()}
Inbar Rose

@InbarRose İşaret ettiğiniz için teşekkürler -.-
Shady Xu

Sadece gelecek kuşak adına Python Sohbeti'nde
Inbar Rose

Yanıtlar:


471

Bir jeneratör ifadesi kullanabilirsiniz:

tuple(i for i in (1, 2, 3))

ancak jeneratör ifadeleri için parantez zaten alınmıştı.


15
Bu argüman, biz bir liste-anlama çok gereksizdir söyleyebiliriz: list(i for i in (1,2,3)). Gerçekten bunun için temiz bir sözdizimi olmadığı için düşünüyorum (ya da en azından kimse bunu düşünmedi)
mgilson

79
Bir liste ya da küme ya da anlama, belirli bir tür çıktı üreten bir üreteç ifadesini kullanmak için sadece sözdizimsel şekerdir. list(i for i in (1, 2, 3))bir liste, bir grup set(i for i in (1, 2, 3))çıktısı üreten bir jeneratör ifadesidir . Bu, anlama sözdiziminin gerekli olmadığı anlamına mı geliyor? Belki hayır, ama çok kullanışlı. Nadir durumlarda bunun yerine bir tupa ihtiyacınız vardır, jeneratör ifadesi bunu yapar, açıktır ve başka bir destek veya braketin icat edilmesini gerektirmez.
Martijn Pieters

16
Cevap açıktır çünkü tuple sözdizimi ve parantez belirsizdir
Charles Salvia

19
Bir kavrama kullanma ve bir yapıcı + jeneratör kullanma arasındaki fark, performansı önemsiyorsanız daha incedir. Anlaşmalar, bir kurucuya geçirilen bir jeneratöre kıyasla daha hızlı konstrüksiyona neden olur. İkinci durumda, Python'da fonksiyonlar oluşturuyorsunuz ve fonksiyonlar pahalı. [thing for thing in things]daha hızlı bir liste oluşturur list(thing for thing in things). Üçlü kavrama işe yaramaz; tuple(thing for thing in things)gecikme sorunları ve tuple([thing for thing in things])bellek sorunları olabilir.
Justin Turner Arthur

9
@MartijnPieters, Potansiyel olarak yeniden yazabilir misiniz A list or set or dict comprehension is just syntactic sugar to use a generator expression? İnsanların bunları bir amaç için eşdeğer araçlar olarak görmeleri karışıklığa neden oluyor . Son ürün aynı olsa bile, süreçler aslında farklı olduğu için teknik olarak sözdizimsel şeker değildir.
jpp

77

Raymond Hettinger (Python çekirdek geliştiricilerinden biri) son tweet'teki tuples hakkında şunları söylemişti :

#python tip: Genellikle listeler döngü içindir; yapılar için tuples. Listeler homojendir; heterojen tuples. Değişken uzunluk için listeler.

Bu (bana göre), bir dizideki öğeler, bir kuyu, jeneratör tarafından üretilecek kadar ilgili ise, o zaman bir liste olması gerektiği fikrini destekler. Bir tuple tekrarlanabilir ve basit bir liste gibi görünse de, gerçekten bir C yapısının Python eşdeğeri:

struct {
    int a;
    char b;
    float c;
} foo;

struct foo x = { 3, 'g', 5.9 };

Python'da

x = (3, 'g', 5.9)

26
Immutibility özelliği önemli olabilir ve normalde bir liste kullandığınızda bir demet kullanmak için iyi bir neden olabilir. Örneğin, bir diktenin anahtarı olarak kullanmak istediğiniz 5 numaradan oluşan bir listeniz varsa, tuple gitmenin yoludur.
pavon

Raymond Hettinger'dan güzel bir ipucu. Yine de tuple yapıcısını bir jeneratörle kullanmak için, belki de daha büyük başka bir yapıyı açmak için, bir tuple kaydına dönüştürmekle ilgilendiğiniz attritleri tekrarlayarak daha küçük bir yapıya açmak gibi bir kullanım durumu olduğunu söyleyebilirim.
dave

2
@dave Büyük olasılıkla operator.itemgetterbu durumda kullanabilirsiniz .
chepner

@ chepner, anlıyorum. Ne demek istediğime oldukça yakın. Ben sadece tuple(obj[item] for item in items)doğrudan kullanarak vs bir kazanmak çok görmüyorum sadece bir kez yapmak gerekirse eğer bir callable döner . Benim durumumda bunu bir grup kaydı listesi yapmak için bir liste kavrayışına gömüyordum. Bunu kod boyunca tekrar tekrar yapmak gerekirse, itemgetter harika görünüyor. Belki itemgetter her iki şekilde de daha deyimsel olurdu?
dave

Frozenset ile tuple ve listeninkine benzer bir ilişki olduğunu görüyorum. Bu heterojenlik ve değişmezlik hakkında daha azdır - frozensets ve tuples, sözlüklerin anahtarları olabilir, listeler ve kümeler değişebilirlikleri nedeniyle olamaz.
polyglot

56

Python* 3.5'ten bu yana , jeneratör ifadesini açmak için uyarıyı açma sözdizimini de kullanabilirsiniz :

*(x for x in range(10)),

2
Bu harika (ve işe yarıyor), ancak belgelendiği hiçbir yerde bulamıyorum! Bağlantın var mı
felixphew

8
Not: Bir uygulama detay gibi, bu temelde yapıyor aynıdır tuple(list(x for x in range(10)))( kod yolları aynıdır , ikisinin de bina ile, listtek fark son adım bir yaratmak olduğunu olmak üzere tuplegelen listve atmak listne zaman bir tupleçıkış gerekli). Aslında bir çift geçiciden kaçınmayacağınız anlamına gelir.
ShadowRanger

4
@ShadowRanger'ın yorumunu genişletmek için, splat + tuple değişmez sözdiziminin aslında bir jeneratör ifadesini tuple yapıcısına iletmekten biraz daha yavaş olduğunu gösterdikleri bir soru .
Lucubrator

Bunu Python 3.7.3'te deniyorum ve *(x for x in range(10))çalışmıyor. Anladım SyntaxError: can't use starred expression here. Ancak tuple(x for x in range(10))çalışır.
Ryan H.

4
@RyanH. sonuna bir virgül koymalısın.
czheo

27

Bir başka afişin de macmbelirttiği gibi, bir jeneratörden bir demet oluşturmanın en hızlı yolu tuple([generator]).


Performans karşılaştırması

  • Liste anlama:

    $ python3 -m timeit "a = [i for i in range(1000)]"
    10000 loops, best of 3: 27.4 usec per loop
  • Liste kavrayışından alıntı:

    $ python3 -m timeit "a = tuple([i for i in range(1000)])"
    10000 loops, best of 3: 30.2 usec per loop
  • Jeneratör dizisi:

    $ python3 -m timeit "a = tuple(i for i in range(1000))"
    10000 loops, best of 3: 50.4 usec per loop
  • Ambalajdan çıkarma:

    $ python3 -m timeit "a = *(i for i in range(1000)),"
    10000 loops, best of 3: 52.7 usec per loop

Python sürümüm :

$ python3 --version
Python 3.6.3

Dolayısıyla, performans bir sorun olmadıkça, her zaman liste kavrayışından bir grup oluşturmalısınız.


10
Not: tuplelistcomp, final tupleve list. tupleBir genexpr, daha yavaş olsa da, sadece final için ödeme yaparsınız tuple, geçici değil list(genexpr'in kendisi kabaca sabit belleği işgal eder). Genellikle anlamlı değildir, ancak ilgili boyutlar çok büyük olduğunda önemli olabilir.
ShadowRanger

25

Anlama, öğeleri döngüye sokarak veya öğeleri yineleyerek ve bunları bir kaba atayarak çalışır, bir Tuple atamaları alamaz.

Bir Tuple oluşturulduktan sonra eklenemez, genişletilemez veya atanamaz. Bir Tuple üzerinde değişiklik yapmanın tek yolu, nesnelerinden birinin kendisine atanabilmesidir (tuple olmayan bir kaptır). Çünkü Tuple sadece bu tür bir nesneye referans veriyor.

Ayrıca - bir demet, tuple()herhangi bir yineleyici verebileceğiniz kendi yapıcısına sahiptir. Yani bir demet oluşturmak için şunları yapabilirsiniz:

tuple(i for i in (1,2,3))

9
Bazı açılardan katılıyorum (bunun gerekmediği için, çünkü bir liste yapacaktır), ama başka şekillerde de katılmıyorum (değişmez olduğu için akıl yürütme hakkında). Bazı açılardan, değişmez nesneleri anlamak daha mantıklıdır. kim yapar lst = [x for x in ...]; x.append()?
mgilson

@mgilson Bunun söylediklerimle nasıl ilgili olduğundan emin değilim?
Inbar Rose

2
@mgilson tuple aracı, altta yatan uygulama olduğu değişmez ise edilemez bir demet (her seferinde tek parça yapı ima "nesil") "oluşturmak". değişmez anlamına gelir, 3 parça ile değiştirerek 4 parça ile inşa edemez. bunun yerine, bir liste, nesil için tasarlanmış bir şey oluşturarak "nesil" grubunu uygularsınız, daha sonra son parçayı son adım olarak oluşturur ve listeyi atarsınız. Dil bu gerçeği yansıtıyor. Tuplleri C yapıları olarak düşünün.
Scott

2
anlamaların sözdizimsel şekerinin tuples için çalışması mantıklı olsa da, anlama geri dönünceye kadar demeti kullanamazsınız. Etkili bir şekilde değişebilir gibi davranmaz, daha ziyade bir dizi kavrama ipin eklenmesi gibi davranabilir.
uchuugaka

12

Benim en iyi tahminim, parantezleri bitti ve "çirkin" bir sözdizimi ekleyerek warrent için yeterince yararlı olacağını düşünmedim ...


1
Köşeli ayraçlar kullanılmıyor.
uchuugaka

@uchuugaka - Tamamen değil. Karşılaştırma operatörleri için kullanılırlar. Muhtemelen hala belirsizlik olmadan yapılabilir, ama belki de çabaya değmez ...
mgilson

3
@uchuugaka {*()}Çirkin olsa da boş bir set değişmez olarak çalıştığını belirtmek gerekir !
MI Wright

1
Ugh. Estetik bir bakış açısından, ben kısmi olduğumu düşünüyorum set():)
mgilson

1
@QuantumMechanic: Evet, mesele bu; ambalajdan çıkarma genellemeleri, boş "set değişmezini" mümkün kıldı. Not {*[]}diğer seçeneklere kesinlikle daha aşağı olduğu; boş dize ve boş tuple, değişmez, tek tonludur, bu nedenle boş olanı inşa etmek için geçici olmaya gerek yoktur set. Buna karşılık, boş listbir singleton değildir, bu yüzden onu inşa etmek, kullanmak için kullanmak set, sonra yıkmak, tek gözlü maymun operatörünün sağladığı her türlü önemli performans avantajını kaybetmek zorundasınız .
ShadowRanger

8

Tuples etkili bir liste gibi eklenemez.

Bu nedenle, bir grup kavrayışının bir listeyi dahili olarak kullanması ve daha sonra bir gruba dönüştürmesi gerekir.

Şu an yaptığınızla aynı olurdu: tuple ([kavrama])


3

Parantezler bir demet oluşturmaz. aka one = (iki) bir demet değildir. Tek yol ya bir = (iki,) ya da bir = tuple (iki). Yani bir çözüm:

tuple(i for i in myothertupleorlistordict) 

Güzel. neredeyse aynı.
uchuugaka

-1

Bunun açıklık uğruna olduğuna inanıyorum, dili çok farklı sembollerle karıştırmak istemiyoruz. Ayrıca bir tuplekavrayış asla gerekli değildir , bir liste kavrayışının aksine dikte bir kavrayışın aksine, ihmal edilebilir hız farklılıklarıyla bir liste kullanılabilir.


-2

Bir liste kavrayışından tuples üretebiliriz. Aşağıdaki bir sıraya iki sayı ekler ve 0-9 arası sayılardan oluşan bir liste verir.

>>> print k
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> r= [tuple(k[i:i+2]) for i in xrange(10) if not i%2]
>>> print r
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
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.