koddla

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

Python List Comprehension – Liste Üreteçleri

Python’daki list comprehension – liste üreteç yapısı kısa ve sözdizimseldir. Listedeki her bir öğe için bir takım fonksiyonlar çağırarak başka listeler oluşturmak için kullanılabilirler. Bu ifadelerin kullanımını görelim.

Sözdizimi

  • [x + 1 for x in (1, 2, 3)] # liste üreteci, [2, 3, 4] değerini verir
  • (x + 1 for x in (1, 2, 3)) # generator ifadesi, 2, ardından 3, ardından 4 sonucunu verir (yield)
  • [x for x in (1, 2, 3) if x % 2 == 0] # koşullu liste üreteci, [2] değerini verir
  • [x + 1 if x % 2 == 0 else x for x in (1, 2, 3)] # ternery liste üreteci
  • [x + 1 if x % 2 == 0 else x for x in range(-3,4) if x > 0] # ternery ve koşullu liste üreteci
  • {x for x in (1, 2, 2, 3)} # set üreteci {1, 2, 3} verir
  • {k: v for k, v in [(‘a’, 1), (‘b’, 2)]} # dict üreteci, {‘a’: 1, ‘b’: 2} verir (yalnızca python 2.7+ ve 3.0+)
  • [x + y for x in [1, 2] for y in [10, 20]] # İç içe geçmiş döngüler, [11, 21, 12, 22] verir
  • x + y for x in [1, 2, 3] if x > 2 for y in [3, 4, 5]] # 1. döngüde koşul
  • [x + y for x in [1, 2, 3] for y in [3, 4, 5] if x > 2] # 2. döngüde koşul
  • [x for x in xrange(10) if x % 2 == 0] # Döngü sayılarının tek sayı olup olmadığı kontrol edilir

Üreteçler belirli bir dile özgü veri yapılarını veya ifadelerini tanımlayan sözdizimsel yapılardır. Üreteçlerin doğru kullanımı verileri kolayca anlaşılabilir ifadeler halinde yeniden yorumlaya yardımcı olur. İfadeler olarak kullanılabilirler:

  • tanımların sağ tarafında
  • fonksiyon çağırmalar için argümanlar olarak
  • lambda fonksiyonlarda 
  • bağımsız ifadeler olarak. (Örnek: [print(x) for x in range(10)])

Python Liste Üreteçlerin Temelleri

Python’daki liste üreteçleri listedeki her öğeye işlevler uygulayarak diğer listelerden listeler oluşturmak için kullanılabilirler. En temel formu aşağıdaki gibidir;

[ <ifade> for <eleman> in <iterable> ]

İsteğe bağlı bir ‘if’ koşulu da oluşturulabilir:

[ <ifade> for <eleman> in <iterable> if <koşul> ]

<eleman> doğru olarak değerlendirilirse, <iterable> öğesindeki her <koşul> doğru olarak döndürülür. Tüm sonuçlar yeni listede bir kerede döndürülür. Üreteç ifadeleri tembelce değerlendirilir, ancak liste kavramları tüm yinelemeyi hemen değerlendirir – yineleyicinin uzunluğuyla orantılı bellek tüketir.

Bir kare tam sayı listesi oluşturmak için :

kareler  =  [ x  *  X  for X  in  (1,  2,  3,  4)] 
# kareler: [1, 4, 9, 16]

for İfadesi x için sırayla parantez içindeki her değere (1, 2, 3, 4) ifadeyi uygular. İfadenin sonucu x * x dahili bir liste eklenir. Dahili list tamamlandığında değişkene atanır.

Hız artışının yanı sıra, bir liste üreteci kabaca aşağıdaki for-döngüsüne eşdeğerdir:

kareler  =  [] 
for x  in  (1, 2, 3, 4): 
    kareler.ekle(x*x) 
# kareler: [1, 4, 9, 16]

Her öğeye uygulanan ifade gerektiğinde karmaşık olabilir:

[s.upper() for s in "Hello World"] 
# ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']

# Bir listedeki dizelerin sonundaki virgülleri kaldırın 
[w.strip(',') for w in ['these,', 'words,,', 'mostly', 'have,commas,']] 
# ['these', 'words', 'mostly', 'have,commas']

# Harfleri kelimelerde daha makul bir şekilde düzenleyin - alfabetik sırayla 
cümle  =  "Güzel, çirkin olmaktan iyidir" 
["".join(sorted(word, key = lambda x: x.lower())) for word in sentence.split()] 
# ['aBefiltuu', 'is', 'beertt', 'ahnt', 'gluy']

Else ifadesi

else liste üreteç yapılarında kullanılabilir, ancak sözdizimi konusunda dikkatli olunmalıdır. If/else yan tümceleri fordan önce kullanılmalıdır, sonra değil:

# apple kelimesinden sessiz harflerin * ile değiştirildiği bir liste oluşturalım
# 'apple' --> ['a', '*', '*', '*' ,'e']

[x for x in 'apple' if x in 'aeiou' else '*']
#SyntaxError: invalid syntax

# if/else kullanırken fordan önce kullanılmalı
[x if x in 'aeiou' else '*' for x in 'apple']
#['a', '*', '*', '*', 'e']

Bunun normal bir dil yapısı değil, koşullu bir ifade olduğunu unutmayın. Yani burada anlama yönelik bir sözdizimi oluşturmuyoruz. Oysa if, for…in liste anlamalarının bir parçasıdır ve kaynak yinelemeden öğeleri filtrelemek için kullanılır.

Çift Yineleme

Çift yineleme sırası [... for x in ... for y in ...] doğal gelebilir veya karşı sezgisel de olabilir. Genel kural,  for döngüsünün eşdeğerini oluşturmaktır:

def foo(i):
    return i, i + 0.5

for i in range(3):
    for x in foo(i):
        yield str(x)

Bu olur:

[str(x)
    for i in range(3)
        for x in foo(i)
]

Bu, [str(x) for i in range(3) for x in foo(i)] olarak tek bir satıra sıkıştırılabilir.

Çok satırda liste üreteçleri

Daha karmaşık liste üreteçleri istenmeyen bir uzunluğa ulaşabilir veya daha az okunabilir hale gelebilir. Örneklerde daha az yaygın olsa da, bir liste üreteçlerini aşağıdaki gibi birden çok satıra bölmek mümkündür:

[ 
    X  for  X 
    in  'foo' 
    ise  X  değil  de  'bar' 
]

Koşullu Liste Üreteçleri

Bir liste üretecinde değerleri filtrelemek için bir veya daha fazla koşul ekleyebilirsiniz. 

[<deyim> for <öğe> in <yinelenen> if <koşul>]

<yinelenen> içerisindeki her bir <öğe> için; <koşul>, True olarak değerlendirilirse, <deyimi> (genellikle öğenin bir işlevi) döndürülen listeye ekle. 

Örneğin, bir tamsayı dizisinden çift sayıları ayıklamak için aşağıdakini yaparız:

[x for x in range(10) if x % 2 == 0]
# Çıktı: [0, 2, 4, 6, 8]

Aşağıdaki kod bloğu da yukarıdaki koda eşdeğerdir:

cift_sayilar = [] 
for x in range(10):
    if x % 2 == 0:
        cift_sayilar.append(x)

print(cift_sayilar)
# Çıktı: [0, 2, 4, 6, 8]

Ayrıca, koşullu liste ürerteci için [e for x in y if c] ifadesi ile list(filter(lambda x: c, map(lambda x: e, y))) ifadesi birbirine eşdeğerdir.

Burada uyguladığımız yöntemin, liste üreteçlerinde kullanabileceğimiz ... if ... else ... koşullu ifadelerinden (bazen üçlü ifade olarak da bilinir) oldukça farklı olduğunu unutmayın. Aşağıdaki örneği göz önünde bulunduralım:

[x if x % 2 == 0 else None for x in range(10)]
# Çıktı: [0, None, 2, None, 4, None, 6, None, 8, None]

Burada koşullu ifade bir filtre değil, liste öğeleri için kullanılacak değeri belirleyen bir işleçtir:

<değer-if-koşul-true> if <koşul> else <değer-if-koşul-false>

Bu, diğer işleçlerle birleştirirseniz daha belirgin hale gelir:

[2 * (x if x % 2 == 0 else -1) + 1 for x in range(10)]
# Çıktı: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Python 2.7 kullanıyorsanız xrangerange‘e göre çeşitli nedenlerden daha iyi sonuçlar verebilir (xrange belgeleri).

Yukarıdaki koda eşdeğerdir:

sayilar = []
for x in range(10):
    if x % 2 == 0:
        gecici = x
    else:
        gecici = -1
    sayilar.append(2 * gecici+ 1)
print(sayilar)
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Üçlü ifadeleri ve koşulları birleştirebilirsiniz. Üçlü işleç filtre uygulanmış sonuç üzerinde de çalışır: 

[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Çıktı: ['*', '*', 4, 6, 8]

Aynı sonucu sadece üçlü operatör kullansaydık elde edemezdik:

[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Çıktı:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']

Liste Üreteçlerinde İç İçe Döngüler

Liste üreteçlerinde iç içe döngüler de kullanabilir. Bir liste üreteci içinde herhangi bir sayıda for döngüsü kodlayabilirsiniz. Ayrıca her döngü isteğe bağlı bir koşul testine de sahip olabilir. Bunu yaparken, for yapılarının sırası bir dizi iç içe for deyimi yazarken olduğu gibidir. Liste üretecinin genel yapısı aşağıdaki gibi olacaktır: 

[ expression for target1 in iterable1 [if condition1]
             for target2 in iterable2 [if condition2]...
             for targetN in iterableN [if conditionN] ]

Örneğin, aşağıdaki kod birden çok for ifadesi ile listeyi tek boyutat indirir:

data = [[1, 2], [3, 4], [5, 6]]
output = []
for each_list in data:
    for element in each_list:
        output.append(element)
print(output)
# Çıktı: [1, 2, 3, 4, 5, 6]

birden çok for kullanarak yazılan bir eşdeğer liste üreteci aşağıdaki gibi olabilirdi:

data = [[1, 2], [3, 4], [5, 6]]
output = [element for each_list in data for element in each_list]
print(output)
# Çıktı: [1, 2, 3, 4, 5, 6]

Hem ilk yöntemde hem de liste üretecinde, dış döngü önce çalışır.

Daha kompakt olmasının yanı sıra, iç içe geçmiş üreteçler önemli ölçüde daha hızlıdır.

In [1]: data = [[1,2],[3,4],[5,6]]
In [2]: def f():
   ...:     output=[]
   ...:     for each_list in data:
   ...:         for element in each_list:
   ...:             output.append(element)
   ...:     return output
In [3]: timeit f()
1000000 loops, best of 3: 1.37 µs per loop
In [4]: timeit [inner for outer in data for inner in outer]
1000000 loops, best of 3: 632 ns per loop

Yukarıdaki işlev çağrısının yükü yaklaşık 140ns’dir.


Satır içi koşul ifadeleri de benzer şekilde iç içe yazılabilir:

data = [[1], [2, 3], [4, 5]]
output = [element for each_list in data if len(each_list) == 2
                for element in each_list if element != 5]
print(output)
# Çıktı: [2, 3, 4]

Ancak okunabilirlik adına, geleneksel for-loop’ları kullanmayı düşünmelisiniz. Bu, özellikle iç içe yerleştirme 2’den fazla düzey içerirse (daha derin olduğunda) ve/veya üreteç mantığı çok karmaşık olduğunda geçerlidir. Birden çok iç içe geçmiş döngü listesi üreteci hataya eğilimli olabilir veya beklenmeyen bir sonuç verebilir.

Koşul ifadelerinde yineleme ve masraflı işlemlerden kaçının

Aşağıdaki liste üretecini göz önünde bulundurun:

>>> def f(x):
...     import time
...     time.sleep(.1)       # masraflı bir işlemi simüle ettik
...     return x**2

>>> [f(x) for x in range(1000) if f(x) > 10]
[16, 25, 36, ...]

Bu kod 1.000’e kadar olan her x değeri için iki f(x) çağrısıyla sonuçlanır: biri f(x) değerini oluşturmak için, diğeri ise if koşulunu denetlemek için. f(x) özellikle pahalı bir işlemse, bunun önemli oranda performans etkileri olabilir. Daha da kötüsü, eğer f(x)‘i çağırmanın yan etkileri de varsa, şaşırtıcı sonuçlara sahip olabilir. 

Bunun yerine, bir “ara yinelenebilir” oluşturarak her değeri için pahalı işlemi aşağıdaki gibi değerlendirebiliriz:

>>> [v for v in (f(x) for x in range(1000)) if v > 10]
[16, 25, 36, ...]

Veya yerleşik harita (map) eşdeğerini kullanarak:

>>> [v for v in map(f, range(1000)) if v > 10]
[16, 25, 36, ...]

Daha okunabilir bir kodla sonuçlanabilecek başka bir yol ise kısmi sonucu (önceki örnekte v) bir yinelemeye (liste veya dizi gibi) koymak ve sonra bunun üzerinde yinelemektir. v yinelemedeki tek öğe olacağından, artık yavaş çalışan işlevimiz yalnızca bir kez hesaplanır:

>>> [v for x in range(1000) for v in [f(x)] if v > 10]
[16, 25, 36, ...]

Ancak, pratikte, kod mantığı daha karmaşık olabilir ve okunabilir tutmak önemlidir. Genel olarak, karmaşık bir tek satırlık kod yerine ayrı bir üreteç işlevi önerilir:

>>> def process_prime_numbers(iterable):
...     for x in iterable:
...         if is_prime(x):
...             yield f(x)
...
>>> [x for x in process_prime_numbers(range(1000)) if x > 10]
[11, 13, 17, 19, ...]

Birden çok kez bilgi işlem yapmayı önlemenin bir başka yolu da @functools.lru_cache()(Python 3.2+) dekoratörünü kullanmaktır. Bu şekilde, f(x) çıktısı zaten bir kez hesaplanmış olduğundan, özgün liste üretecinin ikinci işlev çağrısı sözlük araması kadar hızlı olacaktır. Bu yaklaşım, verimliliği artırmak için ezberleme (memoization) kullanır. Bu şekilde üreteç ifadelerini kullanmakla karşılaştırılabilir performans sağlar. 

Örneğin aşağıdaki liste öğelerini toplamamız gereksin.

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]

Kullanabileceğimiz yöntemlerden bazıları şöyle olabilir:

reduce(lambda x, y: x+y, l)

sum(l, [])

list(itertools.chain(*l))

Ancak liste üreteci en iyi zaman karmaşıklığını sağlayacaktır.

[item for sublist in l for item in sublist]

+ işlemi tabanlı kısayollar (toplam kullanımı da dahil) L tane alt liste olduğunda O(L^2) karmaşıklıktadır ( Çünkü ara sonuç listesi uzamaya devam eder ve her adımda yeni bir ara sonuç listesi nesnesi ayrılır. Önceki ara sonuçtaki tüm öğeler kopyalanır – ve sonunda birkaç yeni öğe eklenir. Bu nedenle (basitlik için ve genellik kaybı olmadan) her biri I öğelerinin L alt listelerine sahip olduğumuzda: ilk I öğeleri L-1 kez ileri geri kopyalanır, ikincisi L-2 kez öğe gösterir, vb. toplam kopya sayısı, x için x toplamının 1’den L’ye kadar hariç, yani I * (L**2)/2’nin çarpımdır.)

Liste üreteçleri ise yalnızca bir kez bir liste oluştururlar ve her öğeyi (orijinal yerinden sonuç listesine) tam olarak bir kez kopyalar.

Üreteç İfadeleri (Generator)

Üreteç (generator) ifadeleri liste üreteçlerine çok benzer. Temel fark, üreteç ifadelerinin aynı anda bir sonuç kümesi oluşturmamasıdır; yinelenebilen bir üreteç nesnesi (generator object) oluştururlar.

Örneğin, aşağıdaki koddaki farka bakın:

# liste üreteci
[x**2 for x in range(10)]
# Çıktı: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# üreteç ifadesi
(x**2 for x in xrange(10))
# Çıktı: <generator object <genexpr> at 0x11b4b7c80>

Bunlar çok farklı iki nesnedir:

  • liste üreteci bir list nesnesi döndürür, üreteç ifadesi ise generator objesi.
  • generator nesnelerindeki öğelere index ile ulaşılmaz ve öğeleri sırayla almak için next işlevi kullanır.

Not: xrange‘i bir generator nesnesi oluşturduğundan kullanıyoruz. range kullanırsak bir liste oluşturulur.

g = (x**2 for x in xrange(10))
print(g[0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'generator' object has no attribute '__getitem__'
g.next()  # 0
g.next()  # 1
g.next()  # 4
...
g.next()  # 81

g.next()  # StopIteration hatası verir
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

NOT: Iterator.next() ve xrange() Python 3’te bulunmadığından, g.next() işlevi next(g) ve xrange ile değiştirilmelidir.

Bunların her ikisi de benzer şekilde yinelenebilir:

for i in [x**2 for x in range(10)]:
    print(i)

"""
Çıktı:
0
1
4
...
81
"""
for i in (x**2 for x in xrange(10)):
    print(i)

"""
Out:
0
1
4
.
.
.
81
"""

Kullanım örnekleri 

Üreteç ifadeleri tembelce değerlendirilir, bu da her değeri yalnızca üreteç yinelendiğinde ürettikleri ve döndürdükleri anlamına gelir. Bu genellikle büyük veri kümeleri arasında yineleme yaparken, bellekte veri kümesinin bir kopyasını oluşturma gereksiniminden kaçınmak için yararlıdır:

for square in (x**2 for x in range(1000000)):
    # bir şeyler 

Başka bir yaygın kullanım örneği ise, gerekli değilse, tüm öğeler üzerinde yinelemeyi önlemektir. Aşağıdaki örnekte, uzak bir API’den get_objects() ile öğe alınıyor. API’den gelen bilgi binlerce nesne içerebilir. Ancak bunlar tek tek alınmalıdır ve yalnızca bir koşul ile eşleşen bir nesne olup olmadığını bilmemiz gerekir.

def get_objects():
    """API'den teker teker öğe al"""
    while True:
        yield get_next_item()

def object_matches_pattern(obj):
    # koşul değerlendir
    return matches_pattern

def right_item_exists():
    items = (object_matched_pattern(each) for each in get_objects())
    for item in items:
        if item.is_the_right_one:
return True
return False

Sözlük Üreteçleri

Sözlük üreteçleri, liste yerine sözlük nesnesi üretmesi dışında liste üreteçlerine benzer.

Temel bir örneğe bakalım:

{x: x * x for x in (1, 2, 3, 4)}
# Çıktı: {1: 1, 2: 4, 3: 9, 4: 16}

veya;

dict((x, x * x) for x in (1, 2, 3, 4))
# Çıktı: {1: 1, 2: 4, 3: 9, 4: 16}

Bir liste üretecinde olduğu gibi, sadece bazı ölçütleri karşılayan dict öğelerini üretmek için dict üreteci içinde koşullu bir ifade kullanabiliriz.

{isim: len(isim) for isim in ('Koddla', 'Python', 'Dersi') if len(isim) > 5}  
# Çıktı: {'Koddla': 6, 'Python': 6}

Veya

dict((isim, len(isim)) for isim in ('Koddla', 'Python', 'Dersi') if len(isim) > 5)
# Çıktı: {'Koddla': 8, 'Python': 6}

Sözlükle başlayıp filtreleme ile anahtar-değer çiftini sözlük üretecinde kullanma

baslangic_sozlugu = {'x': 1, 'y': 2}
{anahtar: deger for anahtar, deger in baslangic_sozlugu.items() if anahtar == 'x'}
# Çıktı: {'x': 1}

Sözlüğün anahtar ve değerini değiştirme (sözlüğü ters çevir)

Basit hashable değerleri içeren bir dict’iniz varsa (yinelenen değerlerin beklenmeyen sonuçları olabilir):

my_dict = {1: 'a', 2: 'b', 3: 'c'}

ve kodlama stilinize bağlı olarak çeşitli yaklaşımlarda kullanabileceğiniz anahtarları ve değerleri değiştirmek istediniz:

  • swapped = {v: k for k, v in my_dict.items()}
  • swapped = dict((v, k) for k, v in my_dict.iteritems())
  • swapped = dict(zip(my_dict.values(), my_dict))
  • swapped = dict(zip(my_dict.values(), my_dict.keys()))
  • swapped = dict(map(reversed, my_dict.items()))
print(swapped)
# Çıktı: {a: 1, b: 2, c: 3}

Sözlüğünüz büyükse, itertools kullanmayı deneyebilirsiniz – izip veya imap‘e göz atın.

Sözlükleri Birleştirme

Sözlükleri birleştirin ve (isteğe bağlı olarak) eski değerlerin üzerine yazın.

dict1 = {'w': 1, 'x': 1}
dict2 = {'x': 2, 'y': 2, 'z': 2}

{k: v for d in [dict1, dict2] for k, v in d.items()}
# Çıktı: {'w': 1, 'x': 2, 'y': 2, 'z': 2}

Burada sözlük açma (PEP 448) da tercih edilebilir.

{**dict1, **dict2}
# Çıktı: {'w': 1, 'x': 2, 'y': 2, 'z': 2}

Set Üreteçleri

Set üreteçleri liste ve sözlük üreteçlerinen benzer şekilde çalışır, ancak setler sıralanmamış koleksiyonlardır.

# range(5) içerisindeki her değeri içeren bir set
{x for x in range(5)}
# Çıktı: {0, 1, 2, 3, 4}

# 1 ile 10 arasındaki her çift sayıyı içeren bir set:
{x for x in range(1, 11) if x % 2 == 0}
# Çıktı: {2, 4, 6, 8, 10}

# Bir dizedeki özgün alfabetik karakterleri döndürür:
text = "When in the Course of human events it becomes necessary for one people..."
{ch.lower() for ch in text if ch.isalpha()}
# Çıktı: set(['a', 'c', 'b', 'e', 'f', 'i', 'h', 'm', 'l', 'o',
#           'n', 'p', 's', 'r', 'u', 't', 'w', 'v', 'y'])

Setlerin sıralanmamış olduğunu unutmayın. Bu, kümedeki sonuçların sırasının yukarıdaki örneklerde sunulandan farklı olabileceği anlamına gelir.

Not: Set üreteçleri, 2.0’da eklenen liste anlamalarının aksine Python 2.7+’dan beri kullanılabilir.

set(x for x in range(5))
# Çıktı: {0, 1, 2, 3, 4}

Demet – tuple içeren üreteçler

Liste üreteçlerindeki for döngüsü birden fazla değişken belirtebilir: 

[x + y for x, y in [(1, 2), (3, 4), (5, 6)]]
# Çıktı: [3, 7, 11]

[x + y for x, y in zip([1, 3, 5], [2, 4, 6])]
# Çıktı: [3, 7, 11]

Bu durum normal döngülere benzerdir:

for x, y in [(1,2), (3,4), (5,6)]:
    print(x+y)
# 3
# 7
# 11

Ancak, üreteçlerde üreteci başlatan ifade bir demet ise, bu ifade parantez içine alınmalıdır:

[x, y for x, y in [(1, 2), (3, 4), (5, 6)]]
# SyntaxError: yanlış yazım hatası

[(x, y) for x, y in [(1, 2), (3, 4), (5, 6)]]
# Çıktı: [(1, 2), (3, 4), (5, 6)]

Filter ve map ile liste üreteçleri

filter veya map işlevleri genellikle liste üreteçleri ile değiştirilmelidir. Guido Van Rossum bunu 2005’te yazdığı bir açık mektupta iyi anlatıyor: 

filter(P, S) neredeyse her zaman [x for x in S if P(x)] olarak yazılır ve bu, en yaygın kullanım şekillerinde büyük avantajlara sahiptir. Örneğin x==42 için bir lambda tanımlamak okuyucu açısından çok daha fazla çaba gerektirir (ayrıca lambda liste üretecinden daha yavaştır). Bu durum S’deki x’ler için F(x) şeklinde açılan map(F, S) ifadesi için daha fazla geçerlidir.

Aşağıdaki kod satırları “pythonik değil“dirler. Ve birçok piton okuyucusunda hata verirler.

filter(lambda x: x % 2 == 0, range(10)) # çift sayılar < 10
map(lambda x: 2*x, range(10)) # her sayıyı 2 ile çarp
reduce(lambda x,y: x+y, range(10)) # listedeki tüm öğelerin toplamı

Önceki alıntıdan öğrendiklerimize bakarak, bu ifadeleri eşdeğer liste üreteçlerine dönüştürebiliriz; ayrıca lambda işlevlerini kaldırarak kodu daha okunabilir hale getiririz.

# Filter:
# P(x) = x % 2 == 0
# S = range(10)
[x for x in range(10) if x % 2 == 0]

# Map
# F(x) = 2*x
# S = range(10)
[2*x for x in range(10)]

Zincirleme işlevlerle uğraşırken okunabilirlik daha da önemli hale gelir. Okunabilirlik nedeniyle, bir haritanın veya filtre işlevinin sonuçlarının bir sonrakine geçmesi gerekir; basit durumlarda, bunlar tek bir liste üreteci ile değiştirilebilir. Ayrıca, zincirli Harita ve Filtre süreçleri muhakeme yaparken daha fazla bilişsel yük getirir. Sürecimizin sonucunun ne olduğunu listeden ise kolayca anlayabiliriz.

# Map & Filter
filtered = filter(lambda x: x % 2 == 0, range(10))
results = map(lambda x: 2*x, filtered)

# List comprehension
results = [2*x for x in range(10) if x % 2 == 0]

Yeniden Düzenleme – Kısayol 

  • map
map(F, S) == [F(x) for x in S]
  • filter
filter(P, S) == [x for x in S if P(x)]

F ve P giriş değerlerini dönüştüren ve bool döndüren ifadeler.

Liste üreteçleri ile öğe sayma

Bir yinelemedeki, bazı koşulları karşılayan öğelerin sayısını saymak istediğimizde de üreteçleri kullanabiliriz:

# range(1000) içerisindeki çift olan ve 9 rakamını içeren sayıları say:
print (sum(
    1 for x in range(1000) 
    if x % 2 == 0 and
    '9' in str(x)
))
# Çıktı: 95

Yukarıdaki deyim şöyle özetlenebilir:

  1. range(1000)‘deki öğeler üzerinde yineleme.
  2. Gerekli tüm if koşullarını birleştirme.
  3. Koşulları karşılayan her öğede 1 döndürmek için ifade olarak 1 kullanma.
  4. Koşulları karşılayan madde sayısını belirlemek için tüm 1 ifadelerini toplama.

Not: Burada 1’leri bir listede toplamıyoruz (köşeli ayraçların yokluğuna dikkat edin), ancak bunları doğrudan onları toplayan sum işlevine geçiriyoruz. Buna, liste üretecine benzer bir üreteç ifadesi denir. 

Listedeki Öğelerin Türlerini Değiştirme

Nicel veriler genellikle işlemeden önce sayısal türlere dönüştürülmesi gereken dizeler olarak okunur. Liste öğelerinin türleri Liste üreteçleri veya map() işleviyle dönüştürülebilir.

# string'den int'e
items = ["1","2","3","4"]
[int(item) for item in items]
# Çıktı: [1, 2, 3, 4]

# string'den float'a.
items = ["1","2","3","4"]
map(float, items)
# Çıktı:[1.0, 2.0, 3.0, 4.0]

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Back to top