Python map fonksiyonu

Python’da map() fonksiyonu, listeler, üreteçler, dizeler vb.

Python’da map() fonksiyonu, listeler, üreteçler, dizeler vb. gibi yinelenebilir bir veri yapısındaki her öğeye aynı prosedürü uygulamak için vardır.

Bir örneğe bakalım: map() bir listedeki her öğe üzerinde yineleme yapabilir ve her öğeye bir işlev uygulayabilir, daha sonra yeni listeyi geri döndürür (size geri verir).

Bir sayı alan, bu sayıya 1 ekleyen ve geri döndüren bir fonksiyonunuz olduğunu düşünün:

def add_one(num):
  new_num = num + 1
  return new_num

Ayrıca elinizde bir sayı listesi de olsun:

my_list = [1, 3, 6, 7, 8, 10]

listedeki her sayıyı artırmak istiyorsak, aşağıdakini yapabiliriz:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

Farkettiğiniz gibi map() iki argümana ihtiyaç duyar. Birincisi bir fonksiyon adı ve ikincisi liste gibi bir veri.

map ile ne yapılır?

Şimdi map()’in yapabileceği diğer bazı harika şeyleri görelim. map() birden fazla yinelenebilir veri (listeler, dizeler, vb.) alabilir ve her bir yinelenebilir veriden bir öğeyi argüman olarak bir fonksiyona aktarabilir.

Üç listemiz var:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map() size belirli bir indeksteki öğelerin eklenmesini tutan yeni bir liste oluşturabilir.

Şimdi map()’in bir fonksiyona ihtiyacı olduğunu hatırlayın. Bu sefer yerleşik sum() fonksiyonunu kullanacağız. map() çalıştırıldığında aşağıdaki sonuç elde edilir:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

UNUTMAYIN:
Python 2’de map(), en uzun listeye göre yineleme yapar (listelerin öğelerini gözden geçirir) ve daha kısa listeler için fonksiyona None geçirir, bu nedenle fonksiyonunuz None’ı aramalı ve işlemelidir, aksi takdirde hata alırsınız. Python 3’te map() en kısa listeyi bitirdikten sonra duracaktır. Ayrıca, Python 3’te map() bir liste değil, bir yineleyici döndürür.

map mi yoksa list comprehension mı?

Önce kısaca cevaba geçelim, sonra detaylara bakalım. map herhangi bir yinelenebilir veri türündeki öğelerin herbirine aynı prosedürü uyguluyordu. Peki bunun bir list comprehension kullanmaktan farkı ne?

map işlevi bir yinelenebilir verinin her öğesine uygulamak ve sonuçların bir listesini döndürmek istediğinizde kullanışlıdır. Bu, bir for döngüsü kullanmaktan ve bir liste oluşturmaktan daha basit ve özlüdür.

for genellikle diğer durumlar için daha okunaklıdır. Bu yüzden map’in uygun olmadığı durumlarda for döngüsü kullanın.

map ne kadar hızlı?

map bazı durumlarda mikroskobik olarak daha hızlı olabilir (bir lambda yazmadığınız ve bir liste üreteci ile aynı işlevi yaptığınızda). Liste kavramaları diğer durumlarda daha hızlı olabilir ve çoğu (hepsi değil) Python kodlayan için bunlar daha direkt ve daha net olarak görülür.

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

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

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

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

Nerede map kullanmalı

  • Genel durum: Neredeyse her zaman python’da bir liste üreteci kullanmak isteyeceksiniz çünkü kodunuzu okuyan acemi programcılar için ne yaptığınız daha açık olacaktır – bunu yukarıda zaten söylemiştik. (Bu, başka deyimlerin geçerli olabileceği diğer diller için geçerli değildir.) Hatta python programcıları için ne yaptığınız daha açık olacaktır, çünkü liste üreteçleri python’da yineleme için fiili standarttır; bunlar beklenir.
  • Daha az yaygın durum: Bununla birlikte, zaten tanımlanmış bir fonksiyonunuz varsa, map kullanmak genellikle mantıklıdır, ancak ‘pitonik olmayan’ olarak kabul edilir. Örneğin, map(sum, myLists) [sum(x) for x in myLists]’den daha zarif/terse bir kullanımdır. Sadece yineleme yapmak için iki kez yazmanız gereken bir kukla değişken (örneğin x… için sum(x) veya … için sum() veya readableName… için sum(readableName)) oluşturmak zorunda kalmamanın zarafetini kazanırsınız. Aynı argüman filter, reduce ve itertools modülündeki herhangi bir şey için de geçerlidir: eğer zaten elinizde bir fonksiyon varsa, devam edebilir ve biraz fonksiyonel programlama yapabilirsiniz. Bu bazı durumlarda okunabilirlik kazandırır, bazı durumlarda ise kaybettirir (örneğin acemi programcılar, çoklu argümanlar)… ancak kodunuzun okunabilirliği büyük ölçüde yorumlarınıza bağlıdır.
  • Neredeyse hiçbir zaman: İşlevsel programlama yaparken map işlevini saf soyut bir işlev olarak kullanmak isteyebilirsiniz, burada map’i eşliyor veya başka bir şekilde map’ten bir işlev olarak bahsederek yararlanıyorsunuzdur. Örneğin Haskell’de, fmap adı verilen bir functor arayüzü, herhangi bir veri yapısı üzerinde eşlemeyi genelleştirir. Bu python’da çok nadirdir çünkü python grameri sizi yineleme hakkında konuşmak için generator-style kullanmaya zorlar; bunu kolayca genelleştiremezsiniz. (Bu bazen iyi bazen de kötüdür.) Muhtemelen map(f, *lists)’in makul bir şey olduğu nadir python örnekleri bulabilirsiniz.
def sumEach(myLists):
    return [sum(_) for _ in myLists]
  • Sadece bir for döngüsü kullanarak: Elbette sadece bir for döngüsü de kullanabilirsiniz. İşlevsel programlama açısından o kadar zarif olmasa da, bazen yerel olmayan değişkenler python gibi zorunlu programlama dillerinde kodu daha anlaşılır hale getirir, çünkü insanlar kodu bu şekilde okumaya çok alışkındır. For-döngüleri ayrıca, liste-anlayışları ve map’in optimize edildiği gibi bir liste oluşturmayan herhangi bir karmaşık işlem yaptığınızda (örneğin toplama veya bir ağaç yapma, vb.) genellikle en verimli olanıdır – en azından bellek açısından verimlidir (zaman açısından değil, bazı nadir patolojik çöp toplama hıçkırıkları dışında en kötü ihtimalle sabit bir faktör bekleyebilirsiniz).

“Pythonizm”

map ve filter ve benzeri fonksiyonlar (çok kullanışlı itertools modülü gibi) muhtemelen stil açısından pythonic olmayan olarak kabul edilir.

Tembellik

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

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

Bunu bir liste üreteci ile yapmayı deneyin:

>>> [str(n) for n in range(10**100)]
# BUNU EVDE DENEMEYİN YOKSA ÜZÜLÜRSÜNÜZ #

Liste üreteçlerinin de doğası gereği tembel olduğunu, ancak python’un bunları tembel olmayan olarak uygulamayı seçtiğini unutmayın. Bununla birlikte, python aşağıdaki gibi üreteç ifadeleri biçiminde tembel liste üreteçlerini destekler:

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

Temel olarak […] sözdizimini liste oluşturucusuna bir oluşturucu ifade iletmek olarak düşünebilirsiniz, list(x for x in range(5)) gibi.

Kısa bir ö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 üreteçleri tembel değildir, bu nedenle daha fazla bellek gerektirebilir. Köşeli parantezler […], özellikle parantez karmaşası içindeyken, genellikle işleri açık hale getirir. Öte yandan, bazen [x for x in…. yazmak gibi ayrıntılı olabilirsiniz. Yineleyici değişkenlerinizi kısa tuttuğunuz sürece, kodunuzu girintilemediğinizde liste üreteçleri genellikle daha anlaşılırdır. Ancak kodunuzu her zaman girintileyebilirsiniz.

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

ya da:

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

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

map artık 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 ne kadar veriye ihtiyacınız olduğunu önceden bilmiyorsanız, python3’teki map (ve python2 veya python3’teki üreteç ifadeleri) gerekli olan son ana kadar değerlerini hesaplamaktan kaçınacaktır. Bu genellikle map kullanımından kaynaklanan ek yükten daha ağır basacaktır. Dezavantajı, çoğu fonksiyonel dilin aksine python’da bunun çok sınırlı olmasıdır: bu avantajı yalnızca verilerinize soldan sağa “sırayla” erişirseniz elde edersiniz, çünkü python üreteç ifadeleri yalnızca x[0], x[1], x[2], …. sırasına göre değerlendirilebilir.

Ancak diyelim ki map etmek istediğimiz önceden yapılmış bir f fonksiyonumuz var ve map’in tembelliğini göz ardı ederek değerlendirmeyi hemen list(…) ile zorluyoruz. Çok ilginç sonuçlar elde ederiz:

% 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çlar AAA/BBB/CCC şeklindedir; burada A, python 3…?? ile 2010 civarında bir Intel iş istasyonunda gerçekleştirilmiştir ve B ve C, python 3.2.1 ile 2013 civarında bir AMD iş istasyonunda, son derece farklı donanımlarla gerçekleştirilmiştir. Sonuç, map ve liste kavramalarının performans açısından karşılaştırılabilir olduğu ve diğer rastgele faktörlerden en güçlü şekilde etkilendiği gibi görünüyor. Söyleyebileceğimiz tek şey, garip bir şekilde, liste üreteçlerinin […] diğer üreteç ifadelerinden (…) daha iyi performans göstermesini beklerken, map’in üreteç ifadelerinden daha verimli olduğu (yine tüm değerlerin değerlendirildiği / kullanıldığı varsayılarak).

Bu testlerin çok basit bir fonksiyonu (identity fonksiyonu) varsaydığını fark etmek önemlidir; ancak bu sorun değil, çünkü fonksiyon karmaşık olsaydı, programdaki diğer faktörlerle karşılaştırıldığında performans ek yükü ihmal edilebilir olurdu. (f=lambda x:x+x gibi diğer basit şeylerle test etmek yine de ilginç olabilir)

Python derlemesini okuma konusunda yetenekliyseniz, perde arkasında gerçekten neler olup bittiğini görmek için dis modülünü kullanabilirsiniz:

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

Görünüşe göre list(…) yerine […] sözdizimini kullanmak daha iyi. Ne yazık ki map sınıfı disassembly için biraz opak gibi, ancak yine de buradaki hız testimizle idare edebiliriz.

Toparlamak gerekirse;

Python 3’te map() bir yineleyici olduğundan, neye ihtiyacınız olduğunu aklınızda tutmanız gerekir: bir yineleyiciye mi bir liste nesnesine mi?.

map() sadece lambda fonksiyonu kullanmıyorsanız liste üreteçlerinden daha hızlıdır.

Zaman karşılaştırmalarına tekrar bakalım.

Setup:

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

Built-in fonksiyon:

%timeit map(sum, x_list)  # yineleyici nesnesi oluşturma
# Çıktı: En yavaş çalışma en hızlı çalışmadan 9,91 kat daha uzun sürdü. 
# Bu, bir ara sonucun önbelleğe alındığı anlamına gelebilir.
# 1000000 döngü, 3'ün en iyisi: Döngü başına 277 ns

%timeit list(map(sum, x_list))  # map ile liste oluşturma
# Çıktı: 1000 döngü, 3'ün en iyisi: Döngü başına 214 µs

%timeit [sum(x) for x in x_list]  # liste üreteci ile liste oluşturma
# Çıktı: 1000 döngü, 3'ün en iyisi: Döngü başına 290 µs

lambda fonksiyonu:

%timeit map(lambda i: i+1, i_list)
# Çıktı: En yavaş çalışma, en hızlı çalışmadan 8,64 kat daha uzun sürdü. 
# Bu, bir ara sonucun önbelleğe alındığı anlamına gelebilir.
# 1000000 döngü, 3'ün en iyisi: Döngü başına 325 ns

%timeit list(map(lambda i: i+1, i_list))
# Çıktı: 1000 döngü, 3'ün en iyisi: Döngü başına 183 µs

%timeit [i+1 for i in i_list]
# Çıktı: 10000 döngü, 3'ün en iyisi: Döngü başına 84,2 µs

Ayrıca, üreteç ifadesi diye bir şey de var (PEP-0289‘a bakın). Bu yüzden bunu da karşılaştırmaya eklemek yararlı olabilir

%timeit (sum(i) for i in x_list)
# Çıktı: En yavaş çalışma, en hızlı çalışmadan 6,66 kat daha uzun sürmüştür. 
# Bu, bir ara sonucun önbelleğe alındığı anlamına gelebilir.
# 1000000 döngü, 3'ün en iyisi: Döngü başına 495 ns

%timeit list((sum(x) for x in x_list))
# Çıktı: 1000 döngü, 3'ün en iyisi: Döngü başına 319 µs

%timeit (i+1 for i in i_list)
# Çıktı: En yavaş çalışma, en hızlı çalışmadan 6,83 kat daha uzun sürmüştür. 
# Bu, bir ara sonucun önbelleğe alındığı anlamına gelebilir.
# 1000000 döngü, 3'ün en iyisi: Döngü başına 506 ns

%timeit list((i+1 for i in i_list))
# Çıktı: 10000 döngü, 3'ün en iyisi: Döngü başına 125 µs

Liste nesnesine ihtiyacınız varsa:

Özel işlevse list comprehension kullanın, yerleşik işlev varsa list(map()) kullanın

Liste nesnesine ihtiyacınız yok, sadece yinelenebilir bir nesneye ihtiyacınız varsa:

map() kullanın!

Bu yazı topluluk tarafından oluşturuldu. Lisans bilgisine bakabilirsiniz. Yanlış veya eksik bilgileri düzenlemek için github üzerinden katkıda bulunabilirsiniz.

Kategoriler: Yazı, Python

Okumaya devam et!

Python101 – Girdi ve Değişkenler

Giriş ve Değişkenler Şimdi karmaşık bir programın zamanı geldi: Çalıştırdığımızda, ekranda aşağıdaki gibi bir şey göreceğiz: Not: Kodu F5 tuşuna basarak çalıştırdıktan sonra, python kabuğu yalnızca aşağıdaki çıktıyı verir: Python kabuğuna adınızı girmeniz ve çıktının geri kalanı için enter tuşuna basmanız gerekir.

Python101 – Merhaba, Dünya! – Python’da İlk Kod

Neler öğreneceksinizBu bölümü okuyup öğrendikten sonra programları bir metin düzenleyicisinde veya IDLE’da nasıl düzenleyeceğinizi, bunları sabit diske nasıl kaydedeceğinizi ve kaydettikten sonra nasıl çalıştıracağınızı öğreneceksiniz.

Python’da bir dizeden boşlukları nasıl kırparız?

Python’da bir dizeden baştaki ve sondaki boşlukları nasıl kaldırırız? Örneğin aşağıdaki dönüşümü yapmak istiyoruz: Peki sadece bir boşluk mu kırpacağız yoksa ardışık tüm boşluklar mı kırpmak istiyoruz? İkincisi ise, dizelere ait bir .

Python’da “open with” ile birden fazla dosyayı nasıl açabiliriz?

Aynı anda birkaç dosya içerisinde değişiklik yapmak istiyoruz.

Yorum Gönderin

E-posta hesabınız yayımlanmayacak.

koddla
Tema Mundana by WowThemes.net.