Liste anlama ve harita karşılaştırması


732

map()Aşırı liste kavrayışı kullanmayı tercih etmek için bir neden var mı ? Bunlardan herhangi biri genel olarak daha verimli midir yoksa genel olarak diğerinden daha fazla pitonik kabul edilir mi?


8
Liste anlama yerine harita kullanırsanız PyLint'in uyardığını unutmayın , bkz . W0141 mesajı .
lumbric

2
@lumbric, emin değilim ama sadece lambda haritada kullanılırsa yapar.
0xc0de

Yanıtlar:


660

mapbazı durumlarda mikroskobik olarak daha hızlı olabilir (amaç için bir lambda yapmadığınızda, ancak aynı işlevi harita ve listcomp'da kullandığınızda). Liste kavrayışları diğer durumlarda daha hızlı olabilir ve çoğu (hepsi değil) pythonista onları daha doğrudan ve daha net olarak görür.

Tam olarak aynı işlevi kullanırken haritanın küçük hız avantajına bir örnek:

$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop

Harita lambdaya ihtiyaç duyduğunda performans karşılaştırmasının nasıl tamamen tersine döndüğüne bir örnek:

$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

39
Evet, gerçekten de dahili Python stil rehberimiz, açıkça harita ve filtreye karşı listelemeleri öneriyor (hatta küçük ama ölçülebilir performans iyileştirme haritasından bahsetmemek bile bazı durumlarda ;-).
Alex Martelli

46
Alex'in sonsuz stil noktalarına kibash etmemek, ancak bazen haritayı okumak bana daha kolay geliyor: data = map (str, some_list_of_objects). Bazı diğerleri ... operator.attrgetter, operator.itemgetter, etc.
Gregg Lind

57
map(operator.attrgetter('foo'), objs)okumak daha kolay [o.foo for o in objs]?!
Alex Martelli

52
@Alex: oBurada olduğu gibi gereksiz isimler vermemeyi tercih ediyorum ve örnekleriniz bunun nedenini gösteriyor.
Reid Barton

29
Bence @GreggLind'in bir str()örneği var.
Eric O Lebigot

474

Kılıflar

  • Yaygın durum : Hemen hemen her zaman, python'da bir liste kavrayışı kullanmak isteyeceksiniz, çünkü kodunuzu okuyan acemi programcılara ne yaptığınız daha açık olacaktır. (Bu, diğer deyimlerin geçerli olabileceği diğer diller için geçerli değildir.) Python programcılarına ne yaptığınız daha açık olacaktır, çünkü liste kavramaları python'da yineleme için fiili standarttır; Onlar edilir beklenen .
  • Daha az yaygın olan durum : Bununla birlikte, zaten tanımlanmış bir işlevinizmap varsa, 'unpythonic' olarak kabul edilmesine rağmen , genellikle kullanımı makul olur . Örneğin map(sum, myLists), daha zarif / kısa [sum(x) for x in myLists]. Sadece yinelemek için iki kez yazmanız gereken bir kukla değişken (örneğin sum(x) for x...veya sum(_) for _...veya sum(readableName) for readableName...) oluşturmamanın zarafetini kazanırsınız . Aynı argüman modül için filterve reduceherhangi bir şey için geçerlidir itertools: zaten kullanışlı bir fonksiyonunuz varsa, devam edip bazı fonksiyonel programlama yapabilirsiniz. Bu, bazı durumlarda okunabilirlik kazanır ve diğerlerinde (örneğin acemi programcılar, çoklu argümanlar) kaybeder ... ancak kodunuzun okunabilirliği büyük ölçüde yorumlarınıza bağlıdır.
  • Neredeyse hiçbir zaman : mapFonksiyonel programlama yaparken, haritaladığınız mapveya köreldiğiniz map, veya mapfonksiyon olarak konuşmaktan başka şekilde faydalandığınız zaman, fonksiyonu saf bir soyut fonksiyon olarak kullanmak isteyebilirsiniz . Örneğin Haskell'de, adı verilen bir functor arabirimi fmap, herhangi bir veri yapısı üzerinde eşlemeyi genelleştirir. Bu python'da çok nadirdir, çünkü python dilbilgisi sizi yineleme hakkında konuşmak için jeneratör tarzı kullanmaya zorlar; kolayca genelleme yapamazsınız. (Bu bazen iyi ve bazen kötüdür.) Muhtemelen map(f, *lists)yapılacak makul bir şey olan nadir piton örnekleriyle karşılaşabilirsiniz . Gelebileceğim en yakın örnek sumEach = partial(map,sum), çok kabaca eşdeğer olan bir astar olan:

def sumEach(myLists):
    return [sum(_) for _ in myLists]
  • Sadece for-loop kullanarak : Tabii ki sadece for-loop kullanabilirsiniz. İşlevsel programlama bakış açısından zarif olmasa da, bazen yerel olmayan değişkenler python gibi zorunlu programlama dillerinde kodu daha net hale getirir, çünkü insanlar kodu bu şekilde okumaya çok alışkındır. For-loop'lar, genellikle, sadece liste kavrama ve harita gibi bir liste oluşturmayan herhangi bir karmaşık işlemi yaparken en etkilidir (örneğin, toplama veya ağaç yapma, vb.) - en azından bellek açısından verimli (bazı nadir patolojik çöp toplama hıçkırıklarını yasaklayan en kötü sabit bir faktörden beklediğim zaman açısından değil).

"Pythonism"

"Pitonik" kelimesinden hoşlanmıyorum çünkü pitoniklerin gözlerimde her zaman zarif olduğunu bulamıyorum. Bununla birlikte, mapve filterbenzer işlevler (çok faydalı itertoolsmodül gibi) muhtemelen stil açısından ses kayıtsız olarak kabul edilir.

Tembellik

Verimlilik açısından, çoğu fonksiyonel programlama yapısı gibi, MAP LAZY OLABİLİR ve aslında python'da tembeldir. Bu, bunu yapabileceğiniz anlamına gelir ( python3'te ) ve bilgisayarınızın belleği tükenmez ve kaydedilmemiş tüm verilerinizi kaybetmez:

>>> map(str, range(10**100))
<map object at 0x2201d50>

Bunu bir liste kavrayışı ile yapmayı deneyin:

>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #

Liste kavrayışlarının da doğal olarak tembel olduğunu, ancak python'un tembel olmayan olarak uygulamayı seçtiğini unutmayın . Bununla birlikte, python, aşağıdaki gibi jeneratör ifadeleri biçimindeki tembel liste anlamalarını destekler:

>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>

Temel [...]olarak sözdizimini, bir oluşturucu ifadesinde liste yapıcısına iletmek gibi düşünebilirsiniz list(x for x in range(5)).

Kısa örnek

from operator import neg
print({x:x**2 for x in map(neg,range(5))})

print({x:x**2 for x in [-y for y in range(5)]})

print({x:x**2 for x in (-y for y in range(5))})

Liste anlamaları tembel değildir, bu nedenle daha fazla bellek gerekebilir (jeneratör anlamalarını kullanmadığınız sürece). Köşeli parantezler [...], özellikle parantez karmaşasında olduğunda, işleri genellikle belirginleştirir. Öte yandan, bazen yazmak gibi ayrıntılı olursunuz [x for x in.... Yineleyici değişkenlerinizi kısa tuttuğunuz sürece, kodunuzu girintilendirmezseniz liste anlaşmaları genellikle daha açıktır. Ancak kodunuzu her zaman girintili yapabilirsiniz.

print(
    {x:x**2 for x in (-y for y in range(5))}
)

veya bir şeyleri parçalayın:

rangeNeg5 = (-y for y in range(5))
print(
    {x:x**2 for x in rangeNeg5}
)

Python3 için verimlilik karşılaştırması

map şimdi tembel:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop            ^^^^^^^^^

Bu nedenle, tüm verilerinizi kullanmayacaksanız veya önceden ne kadar veriye ihtiyacınız olduğunu bilmiyorsanız, mappython3'te (ve python2 veya python3'teki jeneratör ifadeleri) son ana kadar değerlerini hesaplamaktan kaçınır. Genellikle bu genellikle herhangi bir ek yükü kullanımdan daha ağır basacaktır map. Dezavantajı, bunun çoğu işlevsel dilin aksine python'da çok sınırlı olmasıdır: bu avantajı yalnızca verilerinize soldan sağa "sırayla" eriştiğinizde elde edersiniz, çünkü python oluşturucu ifadeleri yalnızca sipariş değerlendirilebilir x[0], x[1], x[2], ....

Ancak diyelim ki fistediğimiz önceden yapılmış bir fonksiyonumuz var mapve mapderhal değerlendirmeyi zorlayarak tembelliklerini görmezden geliyoruz list(...). Çok ilginç sonuçlar elde ediyoruz:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'                                                                                                                                                
10000 loops, best of 3: 165/124/135 usec per loop        ^^^^^^^^^^^^^^^
                    for list(<map object>)

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'                                                                                                                                      
10000 loops, best of 3: 181/118/123 usec per loop        ^^^^^^^^^^^^^^^^^^
                    for list(<generator>), probably optimized

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'                                                                                                                                    
1000 loops, best of 3: 215/150/150 usec per loop         ^^^^^^^^^^^^^^^^^^^^^^
                    for list(<generator>)

Sonuçlarda A / python 3.?.? İle Intel iş istasyonunda A ile gerçekleştirilen AAA / BBB / CCC biçimindedir ve B ve C python 3.2.1 ile 2013 dolaylarında AMD iş istasyonu ile gerçekleştirilmiştir son derece farklı bir donanıma sahip. Sonuç, harita ve liste kavrayışlarının, diğer rastgele faktörlerden en güçlü şekilde etkilenen performansla karşılaştırılabilir olduğu görülmektedir. Anlayabileceğimiz tek şey, garip bir şekilde, liste kavrayışlarının [...]jeneratör ifadelerinden daha iyi performans göstermesini beklerken (...), jeneratör ifadelerinden de mapdaha etkilidir (yine tüm değerlerin değerlendirildiğini / kullanıldığını varsayarak).

Bu testlerin çok basit bir fonksiyon (kimlik fonksiyonu) aldığını fark etmek önemlidir; ancak bu iyi bir durumdur, çünkü fonksiyon karmaşık olsaydı, performans yükü programdaki diğer faktörlere kıyasla önemsiz olacaktır. (Gibi diğer basit şeylerle test etmek hala ilginç olabilir f=lambda x:x+x)

Python montajını okuma becerisine sahipseniz, bu dismodülleri kullanarak perde arkasında neler olup bittiğini görebilirsiniz:

>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 
              3 MAKE_FUNCTION            0 
              6 LOAD_NAME                0 (xs) 
              9 GET_ITER             
             10 CALL_FUNCTION            1 
             13 RETURN_VALUE         
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
  1           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                18 (to 27) 
              9 STORE_FAST               1 (x) 
             12 LOAD_GLOBAL              0 (f) 
             15 LOAD_FAST                1 (x) 
             18 CALL_FUNCTION            1 
             21 LIST_APPEND              2 
             24 JUMP_ABSOLUTE            6 
        >>   27 RETURN_VALUE

 

>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_CONST               0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 
              6 MAKE_FUNCTION            0 
              9 LOAD_NAME                1 (xs) 
             12 GET_ITER             
             13 CALL_FUNCTION            1 
             16 CALL_FUNCTION            1 
             19 RETURN_VALUE         
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
  1           0 LOAD_FAST                0 (.0) 
        >>    3 FOR_ITER                17 (to 23) 
              6 STORE_FAST               1 (x) 
              9 LOAD_GLOBAL              0 (f) 
             12 LOAD_FAST                1 (x) 
             15 CALL_FUNCTION            1 
             18 YIELD_VALUE          
             19 POP_TOP              
             20 JUMP_ABSOLUTE            3 
        >>   23 LOAD_CONST               0 (None) 
             26 RETURN_VALUE

 

>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_NAME                1 (map) 
              6 LOAD_NAME                2 (f) 
              9 LOAD_NAME                3 (xs) 
             12 CALL_FUNCTION            2 
             15 CALL_FUNCTION            1 
             18 RETURN_VALUE 

[...]Sözdizimini kullanmaktan daha iyi görünüyor list(...). Ne yazık ki mapsınıf demontaj için biraz opak, ancak hız testimizle yapabiliriz.


5
"çok yararlı itertools modülü [muhtemelen] tarz olarak ses kayıtsız olarak kabul edilir". Hmm. Ben de "Pythonic" terimini sevmiyorum, bu yüzden bir anlamda ne anlama geldiğini umursamıyorum, ama "Pythonicness" yerleşimlerine göre bunu kullananlar için adil olduğunu düşünmüyorum mapve filterstandart kütüphane ile birlikte itertoolsdoğal olarak kötü tarzı vardır. GvR aslında ya korkunç bir hata ya da sadece performans için söylemedikçe, "Pythonicness" in söylediği tek doğal sonuç, bunu aptal gibi unutmaktır ;-)
Steve Jessop

4
@SteveJessop: Aslında, Guidomapfilter düşmeyi / Python 3 için harika bir fikir olarak düşündü ve sadece diğer Pythonistaların isyanı onları yerleşik ad alanında tuttu ( reducetaşındı functools). Şahsen katılmıyorum ( mapve filterönceden tanımlanmış, özellikle yerleşik işlevler konusunda iyiyim, sadece lambdagerektiğinde bunları kullanmayın ), ancak GvR temel olarak onları yıllarca Pythonic olarak adlandırmadı.
ShadowRanger

@ShadowRanger: doğru, ancak GvR hiç kaldırmayı planlıyor itertoolsmuydu? Bu cevaptan alıntı yaptığım kısım, beni rahatsız eden ana iddiadır. İdeal dünyasında olup olmadığını bilmiyorum mapve filtertamamen hareket edip etmeyeceğini itertoolsya functoolsda tamamen gideceğini bilmiyorum , ama hangisi olursa olsun, bir kere itertoolsbunun tamamen Pythonic olduğunu söylediği zaman, o zaman “Pythonic” in ne olduğunu gerçekten bilmiyorum ama "GvR'nin insanların kullanmasını önerdiklerine" benzer bir şey olabileceğini düşünmüyorum.
Steve Jessop

2
@SteveJessop: Sadece konuşuyordum map/ filterdeğil itertools. Fonksiyonel programlama mükemmel Pythonic olan ( itertools, functoolsve operatortüm akılda fonksiyonel programlama ile özel olarak tasarlanmış ve ben Python her zaman fonksiyonel deyimleri kullanın) ve itertoolsO, özellikle oluyor bir ağrı kendinizi uygulamak olacaktır özellikler sağlar mapve filterjeneratör ifadeleriyle olmanın gereksiz Guido onlardan nefret ediyordu. itertoolsher zaman iyiydi.
ShadowRanger

1
Bir yol olsaydı bu cevabı beğenebilirdim. İyi açıkladı.
NelsonGon

95

Python 2: Liste anlama yerine mapve filterkullanmalısınız.

Bir objektif onlar değiliz rağmen onları tercih etmelidir nedeni "Pythonic" şudur:
Onlar argümanlar gibi işlevleri / lambdas gerektiren yeni bir kapsam tanıtmak .

Bunu bir kereden fazla ısırdım:

for x, y in somePoints:
    # (several lines of code here)
    squared = [x ** 2 for x in numbers]
    # Oops, x was silently overwritten!

ama onun yerine şöyle demiştim:

for x, y in somePoints:
    # (several lines of code here)
    squared = map(lambda x: x ** 2, numbers)

o zaman her şey iyi olurdu.

Aynı değişken adını aynı kapsamda kullandığım için aptal olduğumu söyleyebilirsin.

Ben değildim. Kod başlangıçta iyiydi - ikisi xaynı kapsamda değildi.
Sadece iç bloğu kodun farklı bir bölümüne taşıdıktan sonra sorun ortaya çıktı (okuma: bakım sırasında sorun, geliştirme değil) ve beklemiyordum.

Evet, bu hatayı asla yapmazsanız , liste kavrayışları daha zariftir.
Ama kişisel deneyimlerden (ve başkalarının da aynı hatayı yaptığını görünce), bu hatalar kodunuza girdiğinde geçmeniz gereken acıya değmeyeceğini düşünüyorum.

Sonuç:

Kullanım mapve filter. Kapsamla ilgili zor teşhis edilen küçük hataları önlerler.

Kenar notu:

Durumunuza uygun olup olmadığını imapve ifilter(in itertools) kullanmayı düşünmeyi unutmayın !


7
Bunu işaret ettiğiniz için teşekkürler. Liste kavramanın aynı kapsamda olduğu ve bir sorun olabileceği açıkça görülmemiştir. Bununla birlikte, diğer cevapların bazılarının liste kavrayışının çoğu zaman varsayılan yaklaşım olması gerektiğini açıkça belirttiğini, ancak bunun hatırlanması gereken bir şey olduğunu düşünüyorum. Bu aynı zamanda işlevleri (ve dolayısıyla kapsamı) küçük tutmak ve kapsamlı birim testleri yaptırmak ve iddia beyanlarını kullanmak için iyi bir genel hatırlatmadır.
TimothyAWiseman

13
@wim: Bu sadece Python 2 ile ilgiliydi, ancak geriye doğru uyumlu kalmak istiyorsanız Python 3 için geçerlidir. Bunu biliyordum ve bir süredir Python kullanıyordum (evet, birkaç aydan fazla) ve yine de başıma geldi. Benden daha zeki olan başkalarının da aynı tuzağa düştüğünü gördüm. Eğer bu kadar parlak ve / veya tecrübeli iseniz, bu sizin için bir sorun değildir, o zaman sizin için mutluyum, çoğu insanın sizin gibi olduğunu sanmıyorum. Onlar olsaydı, Python 3'te düzeltmek için böyle bir dürtü olmazdı.
user541686

12
Üzgünüm ama bunu 2012'nin sonlarında yazdınız, python 3 sahnede olduktan sonra, ve cevap sadece kesme sırasında bir hata tarafından ısırıldığınız için popülaritesi olmayan bir python kodlama stili önerdiğiniz gibi görünüyor. yapıştırma kodu. Hiç parlak ya da deneyimli olduğunu iddia etmedim, sadece cesur iddianın nedenlerinizle haklı olduğunu kabul etmiyorum.
wim

8
@ wim: Ha? Python 2 hala birçok yerde kullanılıyor, Python 3'ün var olması bunu değiştirmiyor. Ve "Python'u birkaç aydan fazla kullanan herkes için tam olarak ince bir hata değil" dediğinde, bu cümle tam anlamıyla "bu sadece deneyimsiz geliştiricilerle ilgilidir" (açıkça değil). Ve kayıt için, cevabı açıkça okumadınız, çünkü cesurca hareket ettiğimi söyledim, kopyalama değil, kod. Kopyala-yapıştır hataları diller arasında oldukça eşittir. Bu tür böcek, kapsamı nedeniyle Python'a daha özeldir; daha incedir ve unutmak ve kaçırmak daha kolaydır.
user541686

3
Hala mapve / veya geçişinin mantıklı bir nedeni değildir filter. Eğer bir şey varsa, probleminizi önlemek için en doğrudan ve mantıklı çeviri değil, JeromeJ'un işaret ettiği gibi, sızıntı yapmayan map(lambda x: x ** 2, numbers)bir jeneratör ifadesidir list(x ** 2 for x in numbers). Bak Mehrdad, şahsen bu kadar kişisel oy kullanmayın, buradaki akıl yürütmenize kesinlikle katılmıyorum.
wim

46

Aslında, mapliste kavrayışları Python 3 dilinde oldukça farklı davranıyor. Aşağıdaki Python 3 programına bir göz atın:

def square(x):
    return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))

"[1, 4, 9]" satırını iki kez yazdırmasını bekleyebilirsiniz, bunun yerine "[1, 4, 9]" ve ardından "[]" yazdırabilir. İlk bakışta squaresüç elementten oluşan bir dizi gibi görünüyor, ama ikinci kez boş olan gibi.

Python 2'de map, liste kavrayışlarının her iki dilde yaptığı gibi düz eski bir liste döndürür. En önemli nokta mapPython 3'teki (ve imapPython 2'deki) dönüş değerinin bir liste olmamasıdır - bu bir yineleyici!

Öğeler, bir liste üzerinde yinelemenin aksine bir yineleyici üzerinden yinelendiğinde tüketilir. Bu yüzden squaresson print(list(squares))satırda boş görünüyor .

Özetlemek:

  • Yineleyicilerle uğraşırken, durumsal olduklarını ve onları hareket ettirdikçe mutasyona uğradıklarını hatırlamanız gerekir.
  • Listeler daha öngörülebilirdir, çünkü yalnızca onları açıkça değiştirdiğinizde değişirler; onlar konteynerler .
  • Ve bir bonus: sayılar, dizeler ve tupeller daha fazla tahmin edilebilir, çünkü hiç değişemezler; onlar değerlerdir .

bu muhtemelen liste kavrayışı için en iyi argüman. piton haritası fonksiyonel harita değil, fonksiyonel bir uygulamanın sakat kırmızı başlı üvey çocuğu. Çok üzücü, çünkü kavrayışları gerçekten sevmiyorum.
semiomant

@semiomant Tembel haritanın (python3'teki gibi) istekli haritanın (python2'daki gibi) daha 'işlevsel' olduğunu söyleyebilirim. Örneğin, Haskell'deki harita tembeldir (Haskell'deki her şey tembeldir ...). Her neyse, tembel harita haritaları zincirlemek için daha iyidir - haritaya uygulanan bir haritaya uygulanmış bir haritanız varsa, python2'de ara harita çağrılarının her biri için bir listeniz olurken, python3'te sadece bir sonuç listeniz vardır, bu yüzden daha fazla bellek verimlidir .
MnZrK

Sanırım istediğim, mapbir yineleyici değil, bir veri yapısı üretmek. Ancak tembel yineleyiciler tembel veri yapılarından daha kolaydır. Düşünce için yiyecek. Thanks @MnZrK
semiomant

Haritanın bir yineleyici değil, bir yinelenebilir döndürdüğünü söylemek istersiniz.
user541686

16

Liste kavrayışlarını genel olarak yapmaya çalıştığım şeyden daha etkileyici buluyorum map- her ikisi de bunu başardı, ancak birincisi karmaşık bir lambdaifade ne olabileceğini anlamaya çalışmanın zihinsel yükünü kurtarıyor .

Ayrıca orada Guido'nun lambdas ve fonksiyonel işlevleri Python'a kabul etmekten en çok pişman olduğu şey olarak listelediği bir yerde bir röportaj var (bunu hazırlıksız bulamıyorum) , böylece erdem yoluyla Pythonic olmadıklarını iddia edebilirsiniz. bunun.


9
Evet, iç çek, ama Guido'nun Python 3'te lambda'yı tamamen kaldırmak için orijinal niyeti ona karşı bir lobi barajı aldı, bu yüzden güçlü desteğime rağmen geri döndü - ah, sanırım lambda birçok BASİT vakada çok kullanışlı , sadece sorun SIMPLE sınırlarını aştığında veya bir isme atandığında (bu durumda aptal bir hobbled def kopyası! -).
Alex Martelli

1
Düşündüğünüz röportaj şudur: amk.ca/python/writing/gvr-interview , burada Guido "Bazen katkıları kabul etmekte çok hızlıydım ve daha sonra bunun bir hata olduğunu fark ettim. lambda işlevleri gibi bazı fonksiyonel programlama özellikleri lambda, küçük bir anonim işlev oluşturmanıza olanak tanıyan bir anahtar sözcüktür; harita, filtre ve azaltma gibi yerleşik işlevler, liste gibi bir dizi türü üzerinde bir işlevi çalıştırır. "
J. Taylor

3
@Alex, yılların deneyimine sahip değilim, ancak lambdalardan çok daha karmaşık liste kavrayışları gördüm. Tabii ki, dil özelliklerini kötüye kullanmak direnmek her zaman zor bir caziptir. Liste kavrayışlarının (ampirik olarak) lambdalardan daha fazla istismara eğilimli göründüğü ilginç, ancak neden böyle olması gerektiğinden emin değilim. Ayrıca "azarlanmış" ın her zaman kötü bir şey olmadığını da belirteceğim. "Bu satırın yaptığı şeyler" in kapsamını azaltmak bazen okuyucuyu daha kolay hale getirebilir. Örneğin, constC ++ anahtar kelimesi bu satırlar boyunca büyük bir zaferdir.
Stuart Berg

> guido. Guido'nun aklından çıktığını gösteren başka bir kanıt da bu. Tabii ki lambdao kadar topal hale getirildi (ifade yok ..), kullanımı zor ve yine de sınırlı.
javadba

16

İşte olası bir durum:

map(lambda op1,op2: op1*op2, list1, list2)

karşı:

[op1*op2 for op1,op2 in zip(list1,list2)]

Zip (), harita yerine liste anlayışlarını kullanmakta ısrar ederseniz şımartmanız gereken talihsiz ve gereksiz bir ek yük olduğunu tahmin ediyorum. Birisi bunu olumlu ya da olumsuz olarak açıklarsa harika olur.


"[op1 * op2'den op2, zip olarak op2 (liste1, liste2)]" | s / form / for / Ve zip içermeyen eşdeğer bir liste: (daha az okunabilir) [list1 [i] * list2 [i] aralıktaki i için (len (
liste1

2
İkinci kod alıntısında @andz ve @ weakish'in yorumunda da "için" değil "olmalıdır. Anlamaları listelemek için yeni bir sözdizimsel yaklaşım bulduğumu sanıyordum ...
physicsmichael

4
çok geç bir yorum eklemek için, ziptembel yapabilirsinizitertools.izip
tacaswell

5
Sanırım hala tercih ediyorum map(operator.mul, list1, list2). Bu çok basit sol taraftaki ifadelerde kavrayışların sakıncası olur.
Yann Vernier

1
Haritanın işlevi için girdi olarak birkaç yinelenebilir alabileceğini ve dolayısıyla bir zip'den kaçınabileceğini fark etmemiştim.
bli

16

Herhangi bir eşzamansız, paralel veya dağıtılmış kod yazmayı planlıyorsanız, büyük olasılıkla mapliste kavramasını tercih edersiniz - çoğu eşzamansız, paralel veya dağıtılmış paket mappythonları aşırı yüklemek için bir işlev sağlar map. Daha sonra uygun mapişlevi kodunuzun geri kalanına ileterek, orijinal seri kodunuzu paralel olarak (vb.) Çalıştıracak şekilde değiştirmeniz gerekmeyebilir.



1
Python'un çoklu işleme modülü bunu yapar: docs.python.org/2/library/multiprocessing.html
Robert L.

9

Python 3, map()bir yineleyici olduğundan, neye ihtiyacınız olduğunu aklınızda bulundurmanız gerekir: bir yineleyici veya listnesne.

@AlexMartelli'nin daha önce de belirtildiği gibi , map()yalnızca lambdaişlevi kullanmıyorsanız liste kavrayışından daha hızlıdır .

Size zaman karşılaştırmaları sunacağım.

Python 3.5.2 ve CPython
ben kullandım Jüpiter dizüstü ve özellikle %timeityerleşik sihirli komut
Ölçümler : s == 1000 ms == 1000 * 1000 us = 1000 x 1000 x 1000 ns

Kurmak:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))

Dahili fonksiyon:

%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop

lambda fonksiyon:

%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop

Jeneratör ekspresyonu diye bir şey de vardır, bkz. PEP-0289 . Bu yüzden karşılaştırmaya eklemek yararlı olacağını düşündüm

%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop

%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop

%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop

%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop

listNesneye ihtiyacınız var :

Özel işlevse liste kavrama özelliğini kullanın list(map()), yerleşik işlev varsa kullanın

listNesneye ihtiyacınız yok , sadece tekrarlanabilir olana ihtiyacınız var:

Her zaman kullanın map()!


1

Bir nesnenin yöntemini çağırmak için üç yöntemi karşılaştırarak hızlı bir test çalıştırdı. Bu durumda zaman farkı ihmal edilebilir ve söz konusu fonksiyonun konusudur (@Alex Martelli'nin cevabına bakınız ). Burada, aşağıdaki yöntemlere baktım:

# map_lambda
list(map(lambda x: x.add(), vals))

# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))

# map_comprehension
[x.add() for x in vals]

Liste boyutlarını artırmak için valshem tamsayıların (Python int) hem de kayan nokta numaralarının (Python float) listelerine (değişkente saklanır) baktım . Aşağıdaki kukla sınıf DummyNumdikkate alınır:

class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

    def __init__(self, n):
        self.n = n

    def add(self):
        self.n += 5

Özellikle, addyöntem. __slots__Özelliği, bellek boyutu azaltılması, sınıf (nitelikler) tarafından ihtiyaç duyulan toplam bellek tanımlamak için Python basit bir iyileştirmedir. Ortaya çıkan araziler.

Python nesne yöntemlerini eşleme performansı

Daha önce belirtildiği gibi, kullanılan teknik minimum bir fark yaratır ve sizin için veya belirli durumlarda en okunabilir şekilde kodlamanız gerekir. Bu durumda, liste kavrama ( map_comprehensionteknik), özellikle daha kısa listelerle, bir nesnedeki her iki ekleme türü için en hızlıdır.

Çizim ve verileri oluşturmak için kullanılan kaynak için bu macunu ziyaret edin .


1
Diğer cevaplarda mapdaha önce açıklandığı gibi, sadece fonksiyon aynı şekilde çağrıldığında (yani [*map(f, vals)]vs. [f(x) for x in vals]) daha hızlıdır . Yani list(map(methodcaller("add"), vals))daha hızlı [methodcaller("add")(x) for x in vals]. mapDöngü muadili, bazı ek yükleri önleyebilecek farklı bir çağrı yöntemi kullandığında daha hızlı olmayabilir (örneğin x.add(), methodcallerveya lambda ifadesi ek yükünü önler ). Bu özel test için [*map(DummyNum.add, vals)]hızlı (çünkü olacağını DummyNum.add(x)ve x.add()temelde aynı performansa sahip).
GZ0

1
Bu arada, açık list()çağrılar liste kavrayışlarından biraz daha yavaştır. Adil bir karşılaştırma için yazmanız gerekir [*map(...)].
GZ0

@ GZ0 harika geri bildirim için teşekkürler! Her şey mantıklı ve list()çağrıların genel olarak arttığını bilmiyordum . Cevapları okumak için daha fazla zaman harcamalıydım. Bu testleri adil bir karşılaştırma için tekrar çalıştıracağım, ancak farklar ihmal edilebilir.
craymichael

0

En Pitonik yolun mapve yerine bir liste kavrayışı kullanmak olduğunu düşünüyorum filter. Nedeni listesi comprehensions daha net olmasıdır mapve filter.

In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension

In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter

In [3]: odd_cubes == odd_cubes_alt
Out[3]: True

Gördüğünüz gibi, bir anlayış ihtiyaç olarak fazladan lambdaifade gerektirmez map. İken Ayrıca, bir anlama da kolayca filtreleme sağlar mapgerektirir filterolanak tanımak için.


0

@ Alex-martelli tarafından kodu denedim ama bazı tutarsızlıklar buldum

python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop

Harita çok geniş aralıklar için bile aynı süreyi alırken, liste kavrama özelliğini kullanmak benim kodumda görüldüğü gibi çok zaman alır. Bu yüzden "unpythonic" olarak değerlendirilmenin dışında, haritanın kullanımı ile ilgili herhangi bir performans sorunuyla karşılaşmadım.


3
Bu çok eski bir soru ve bahsettiğiniz cevap büyük olasılıkla mapbir liste döndüren Python 2'ye referansla yazılmıştır . Python 3'te maptembel bir şekilde değerlendirildiğinden, basitçe çağırmak mapyeni liste öğelerinin hiçbirini hesaplamaz, bu yüzden neden bu kadar kısa süreler alırsınız.
kaya3

Sanırım Python 3.x kullanıyorsunuz Bu soruyu sorduğumda Python 3 kısa bir süre önce piyasaya sürülmüştü ve Python 2.x çok standarttı.
TimothyAWiseman
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.