Python'da [] olmadan liste anlama


86

Bir listeye katılmak:

>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'

join tekrarlanabilir olmalıdır.

Görünüşe göre, joinargümanı şu [ str(_) for _ in xrange(10) ]ve bu bir liste anlayışı .

Şuna bak:

>>>''.join( str(_) for _ in xrange(10) )
'0123456789'

Şimdi, joinargümanı sadece str(_) for _ in xrange(10), hayır [], ama sonuç aynı.

Neden? Does str(_) for _ in xrange(10)de bir liste veya bir iterable üretmek?


1
Bunun joinbüyük olasılıkla C'de yazıldığını ve bu nedenle bir liste anlamadan çok daha hızlı çalıştığını tahmin ediyorum ... Test süresi!
Joel Cornett

Görünüşe göre sorunuzu tamamen yanlış okudum. Görünüşe göre benim için bir jeneratör iade ediyor ...
Joel Cornett

18
Sadece bir not: _özel bir anlamı yoktur, normal bir değişken adıdır. Genellikle bir atma adı olarak kullanılır, ancak durum böyle değildir (değişkeni kullanıyorsunuz). Onu bir kodda kullanmaktan kaçınırdım (en azından bu şekilde).
rplnt

Yanıtlar:


69
>>>''.join( str(_) for _ in xrange(10) )

Buna üretici ifadesi denir ve PEP 289'da açıklanmıştır .

Oluşturucu ifadeleri ile liste anlamaları arasındaki temel fark, ilkinin listeyi bellekte oluşturmamasıdır.

İfadeyi yazmanın üçüncü bir yolu olduğunu unutmayın:

''.join(map(str, xrange(10)))

1
Bildiğim kadarıyla, bir jeneratör gibi demet benzeri bir ifade ile üretilebilir ( str(_) for _ in xrange(10) ). Ama kafam karıştı, neden ()atlanabilir join, bu da kodun "" .join ((str (_) for _ in xrange (10))) gibi olması gerektiği anlamına geliyor, değil mi?
Alcott

2
@Alcott Tuple'larla ilgili anlayışım, aslında parantezle değil, virgülle ayrılmış ifadeler listesiyle tanımlandıklarıdır; parantezler yalnızca bir atamadaki değerleri görsel olarak gruplamak veya demet bir işlev çağrısı gibi virgülle ayrılmış başka bir listeye gidiyorsa değerleri gerçekten gruplamak için vardır. Bu genellikle gibi kod çalıştırılarak gösterilir tup = 1, 2, 3; print(tup). Bunu akılda tutarak, forbir ifadenin bir parçası olarak kullanmak oluşturucu oluşturur ve parantezler onu yanlış yazılmış bir döngüden ayırmak için oradadır.
Eric Ed Lohmar

133

Diğer yanıtlayıcılar, bir üreteç ifadesi keşfettiğinizi söylerken haklıydılar (bu, liste anlayışlarına benzer, ancak etrafındaki köşeli parantezler olmadan bir gösterime sahiptir).

Genel olarak, geneksler (sevgiyle bilindikleri için) hafıza açısından daha verimli ve liste anlamalarından daha hızlıdır.

ANCAK, bu durumda ''.join(), bir liste anlama hem daha hızlı hem de daha verimli hafıza. Bunun nedeni, birleştirme işleminin veriler üzerinde iki geçiş yapması gerektiğidir, bu nedenle aslında gerçek bir listeye ihtiyaç duyar. Bir tane verirseniz hemen işine başlayabilir. Bunun yerine bir genexp verirseniz, genexp'i tükenene kadar çalıştırarak bellekte yeni bir liste oluşturana kadar çalışmaya başlayamaz:

~ $ python -m timeit '"".join(str(n) for n in xrange(1000))'
1000 loops, best of 3: 335 usec per loop
~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])'
1000 loops, best of 3: 288 usec per loop

Aynı sonuç itertools.imap ile map karşılaştırılırken de geçerlidir :

~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))'
1000 loops, best of 3: 220 usec per loop
~ $ python -m timeit '"".join(map(str, xrange(1000)))'
1000 loops, best of 3: 212 usec per loop

4
@lazyr İkinci zamanlamanız çok fazla iş yapıyor. Bir genexp'i bir listcomp etrafına sarmayın - sadece bir genexp'i doğrudan kullanın. Garip zamanlamalarınız olduğuna şaşmamalı.
Raymond Hettinger

11
''.join()Bir dizge oluşturmak için yineleyiciden neden 2 geçişin gerekli olduğunu açıklayabilir misiniz ?
ovgolovin

28
@ovgolovin Sanırım ilk geçiş, sıralı dizge için doğru bellek miktarını ayırabilmek için dizelerin uzunluklarını toplamaktır, ikinci geçiş ise ayrı dizeleri ayrılmış alana kopyalamaktır.
Lauritz V. Thaulow

20
@lazyr Bu doğru tahmin. Str.join tam olarak böyle yapar :-)
Raymond Hettinger

4
Bazen SO'da belirli bir yanıtı "beğenme" yeteneğini gerçekten özlüyorum.
Hava

5

İkinci örneğiniz bir liste anlama yerine bir üretici ifadesi kullanıyor. Aradaki fark, listeyi anlama ile bir listenin tamamen oluşturulması ve iletilmesidir .join(). Jeneratör ifadesi ile maddeler tek tek üretilir ve tüketilir .join(). İkincisi daha az bellek kullanır ve genellikle daha hızlıdır.

Olduğu gibi, liste oluşturucu, bir üreteç ifadesi de dahil olmak üzere herhangi bir yinelenebilirliği mutlu bir şekilde tüketecektir. Yani:

[str(n) for n in xrange(10)]

şunun için sadece "sözdizimsel şeker" dir:

list(str(n) for n in xrange(10))

Başka bir deyişle, bir liste anlama, sadece listeye dönüştürülen bir üreteç ifadesidir.


2
Kaputun altında eşdeğer olduklarından emin misin? Timeit diyor ki: [str(x) for x in xrange(1000)]262 usec,: list(str(x) for x in xrange(1000))304 usec.
Lauritz V. Thaulow

2
@lazyr Haklısın. Listeleri anlama daha hızlıdır. Python 2.x'te liste anlamalarının sızmasının nedeni budur. Bu, GVR'nin yazdığı şeydir: "" Bu, liste anlayışlarının orijinal uygulamasının bir ürünüdür; yıllardır Python'un "kirli küçük sırlarından" biriydi. Liste anlamalarını kör edici derecede hızlı hale getirmek için kasıtlı bir uzlaşma olarak başladı ve yeni başlayanlar için yaygın bir tuzak olmasa da, ara sıra insanları kesinlikle soktu
ovgolovin

3
nedenle @ovgolovin listcomp hızlıdır çünkü katılmak o işe başlamak için önce bir liste oluşturmak için vardır. Bahsettiğiniz "sızıntı" bir hız sorunu değildir - sadece döngü indüksiyon değişkeninin listcomp dışında açığa çıktığı anlamına gelir.
Raymond Hettinger

1
@RaymondHettinger O zaman bu kelime ne anlama geliyor? " Liste anlamalarını göz kamaştırıcı derecede hızlı hale getirmek için kasıtlı bir uzlaşma olarak başladı "? Anladığım kadarıyla hız sorunları ile sızıntılarının bir bağlantısı var. GVR ayrıca şunları yazdı: "Oluşturucu ifadeleri için bunu yapamadık. Üreteç ifadeleri, yürütülmesi ayrı bir yürütme çerçevesi gerektiren üreteçler kullanılarak gerçekleştirilir. Bu nedenle, üreteç ifadeleri (özellikle kısa bir dizi üzerinde yineleniyorlarsa) liste anlamalarından daha az etkiliydi . "
ovgolovin

4
@ovgolovin Bir listcomp uygulama ayrıntısından str.join'in neden bu şekilde performans gösterdiğine dair yanlış bir sıçrama yaptınız. Str.join kodundaki ilk satırlardan biri, seq = PySequence_Fast(orig, "");str.join () çağrılırken yineleyicilerin listelerden veya tuple'lardan daha yavaş çalışmasının tek nedeni budur. Daha fazla tartışmak isterseniz bir sohbet başlatabilirsiniz (ben PEP 289'un yazarıyım, LIST_APPEND işlem kodunun yaratıcısıyım ve list () yapıcısını optimize eden kişiyim, bu yüzden biraz var konuya aşinalık).
Raymond Hettinger

5

Belirtildiği gibi, bu bir üreteç ifadesi .

Belgelerden:

Parantezler, yalnızca bir bağımsız değişken içeren çağrılarda ihmal edilebilir. Ayrıntı için Çağrılar bölümüne bakın .


4

Parantez içinde ancak parantez içinde değilse, teknik olarak bir üretici ifadesidir. Üretici ifadeleri ilk olarak Python 2.4'te tanıtıldı.

http://wiki.python.org/moin/Generators

Birleştirmeden sonraki kısım ( str(_) for _ in xrange(10) ), kendi başına bir üretici ifadesidir. Şunun gibi bir şey yapabilirsiniz:

mylist = (str(_) for _ in xrange(10))
''.join(mylist)

ve yukarıdaki ikinci durumda yazdığınızla tamamen aynı anlama gelir.

Jeneratörlerin bazı çok ilginç özellikleri vardır, bunlardan en önemlisi, ihtiyacınız olmadığında tam bir listeyi tahsis etmemeleridir. Bunun yerine, birleştirmek gibi bir işlev, küçük ara parçalar üzerinde işini yaparak, üreteç ifadesinden öğeleri birer birer "pompalar".

Sizin özel örneklerinizde, liste ve oluşturucu muhtemelen çok farklı performans göstermiyor, ancak genel olarak, yapabildiğim her zaman oluşturucu ifadelerini (ve hatta üreteç işlevlerini) kullanmayı tercih ediyorum, çünkü çoğunlukla bir jeneratörün tam listeden daha yavaş olması çok nadirdir. materyalizasyon.


1

Bu bir liste anlayışından ziyade bir oluşturucu. Üreteçler de yinelenebilir, ancak önce tüm listeyi oluşturup daha sonra birleştirmek için geçirmek yerine, xrange'deki her değeri tek tek geçirir, bu çok daha verimli olabilir.


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.