koddla

Yazılımcıları bilgi ile güçlendirir.

Python map fonksiyonu

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

map() fonksiyonu nasıl çalışır ve söz dizimi nedir?

map() fonksiyonunun temel sözdizimi aşağıdaki gibidir:

map(fonksiyon, yinelenebilir)

Burada fonksiyon uygulanacak fonksiyon, yinelenebilir ise fonksiyonun uygulanacağı iterable’dır.

Örneğin, aşağıdaki listeyi göz önünde bulundurun:

sayilar = [1,2,3,4,5]

Listedeki her bir elemanın karesini almak için map() fonksiyonunu şu şekilde kullanabiliriz:

sonuc = map(lambda x: x**2, sayilar)

map() fonksiyonunun map sınıfından bir nesne döndürdüğüne ve bir liste olmadığına dikkat edin. Sonrasında list() fonksiyonunu kullanarak bunu bir listeye dönüştürebilirsiniz.

sonuc_list = list(sonuc)

Örneklerle map() fonksiyonunun kullanımı

Çeşitli yinelenebilir nesnelerin öğelerini dönüştürmek için map() işlevinin nasıl kullanılacağına dair bazı örneklere bakalım:

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. Aşağıdaki örneklerde fonksiyonları lambda ile tanımladık.

Örnek 1: Bir listenin öğelerinin karelerini almak için map() işlevini kullanma

# Bir sayı listesi tanımlamasayilar = [1, 2, 3, 4, 5]

# Listedeki her sayının karesini almak için haritayı kullanınsonuc = map(lambda x: x**2, sayilar)

# map nesnesini listeye dönüştürmesonuc_list = list(sonuc)

print(sonuc_list) # [1, 4, 9, 16, 25]

Örnek 2: Bir dizeler listesini büyük harfe dönüştürmek için map() işlevini kullanma

# Dizelerin bir listesini tanımlamakelime = ["merhaba", "nasılsın", "iyiyim", "seni", "seviyorum"]

# Her dizeyi büyük harfe dönüştürmek için map kullanımısonuc = map(lambda x: x.upper(), kelime)

# map nesnesini listeye dönüştürmesonuc_list = list(sonuc)

print(sonuc_list) # ['MERHABA', 'NASILSIN', 'IYIYIM', 'SENI', 'SEVIYORUM']

Örnek 3: Bir listedeki dizelerin uzunluğunu bulmak için map() işlevini kullanma

# Dizelerin bir listesini tanımlamakelime = ["merhaba", "nasılsın", "iyiyim", "seni", "seviyorum"]

# Her dizenin uzunluğunu bulmak için map kullanınsonuc = map(lambda x: len(x), kelime)

# map nesnesini listeye dönüştürmesonuc_list = list(sonuc)

print(sonuc_list) # [7, 8, 6, 4, 8]

Örnek 4: Listedeki dizelere önek eklemek için map() işlevini kullanma

# Dizelerin bir listesini tanımlamakelime = ["merhaba", "nasılsın", "iyiyim", "seni", "seviyorum"]

# Her dizeye önek eklemek için eşleme kullanınsonuc = map(lambda x: "prefix_" + x, kelime)

# map nesnesini listeye dönüştürmesonuc_list = list(sonuc)

print(sonuc_list) # ['prefix_merhaba', 'prefix_nasılsın', 'prefix_iyiyim', 'prefix_seni', 'prefix_seviyorum']

Örnek 5: Bir listedeki uzunlukları ile dairelerin alanını bulmak için map() fonksiyonunu kullanma

# Yarıçap listesi tanımlamayaricap = [1, 2, 3, 4, 5]

# Her bir dairenin alanını bulmak için haritayı kullanınsonuc = map(lambda x: 3.14*x**2, yaricap)

# map nesnesini listeye dönüştürmesonuc_list = list(sonuc)

print(sonuc_list) # [3.14, 12.56, 28.26, 50.24, 78.5]

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!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Back to top