Python, okunabilir ve aynı zamanda etkili kod yazmayı teşvik eden bir dildir. Bu felsefenin en güzel örneklerinden biri de List Comprehensions (Liste Üreteçleri/Anlayışları)’dır. List comprehensions, mevcut bir yinelenebilir (iterable) nesneden (liste, tuple, string, range vb.) yola çıkarak yeni listeler oluşturmanın son derece özlü (concise), okunabilir (genellikle) ve “Pythonic” (Python’a özgü ve şık) bir yoludur.
Geleneksel for
döngüleri ve append()
metodunu kullanarak liste oluşturmak yerine, list comprehensions ile aynı işlemi tek bir satırda, daha deklaratif (ne yapılacağını söyleyen) bir şekilde ifade edebiliriz. Bu sadece kod satırı sayısını azaltmakla kalmaz, aynı zamanda kodun amacını daha net bir şekilde ortaya koyabilir ve bazı durumlarda performansı artırabilir.
Bu detaylı rehberde, list comprehensions kavramını temelden başlayarak ele alacağız. Geleneksel döngülerle karşılaştıracak, temel sözdizimini parçalarına ayıracak, koşullu ifadelerle nasıl kullanılacağını gösterecek, iç içe geçmiş (nested) comprehensions yapısını inceleyecek ve bu güçlü özelliğin ne zaman ve nasıl en etkili şekilde kullanılacağına dair ipuçları ve en iyi uygulamaları sunacağız. Ayrıca, ilgili kavramlar olan set ve dictionary comprehensions ile generator expressions’a da kısaca değineceğiz.
Bölüm 1: Sorun: Geleneksel Yöntemle Liste Oluşturma
List comprehensions’ın değerini anlamak için önce geleneksel for
döngüsü kullanarak liste oluşturma yöntemini hatırlayalım. Diyelim ki 0'dan 9'a kadar olan sayıların karelerini içeren bir liste oluşturmak istiyoruz.
Geleneksel for döngüsü ile kareler listesi oluşturma
kareler_dongu = [] # 1. Boş bir liste başlat
for sayi in range(10): # 2. Yinelenebilir üzerinde döngü kur
kare = sayi ** 2 # 3. Her öğe için bir işlem yap
kareler_dongu.append(kare) # 4. Sonucu listeye ekle
print(f"Döngü ile oluşturulan kareler: {kareler_dongu}")
Çıktı: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Başka bir örnek: Bir listedeki isimleri büyük harfe çevirelim.
Geleneksel for döngüsü ile büyük harf listesi oluşturma
isimler = ["alice", "bob", "charlie"]
buyuk_isimler_dongu = []
for isim in isimler:
buyuk_isim = isim.upper()
buyuk_isimler_dongu.append(buyuk_isim)
print(f"Döngü ile oluşturulan büyük isimler: {buyuk_isimler_dongu}")
Çıktı: ['ALICE', 'BOB', 'CHARLIE']
Bu yöntemler tamamen geçerli ve işlevseldir. Ancak, özellikle basit dönüşümler için, dört adımlı bir süreç (boş liste oluşturma, döngü kurma, işlem yapma, ekleme) biraz ayrıntılı görünebilir. List comprehensions bu süreci tek, okunabilir bir satıra indirgemeyi hedefler.
Bölüm 2: List Comprehensions: Temel Sözdizimi
Bir list comprehension’ın temel yapısı şöyledir:
[ifade for öğe in yinelenebilir]
Bu yapıyı parçalarına ayıralım:
[]: Sonucun bir liste olacağını belirten köşeli parantezler.
ifade: Yinelenebilir nesnedeki her bir öğe için hesaplanacak olan değer veya işlem. Bu, yeni listenin öğelerini oluşturur.
for öğe in yinelenebilir: Geleneksel for
döngüsüne çok benzer bir yapı. yinelenebilir (liste, tuple, range, string vb.) üzerindeki her bir öğe'yi alır.
Şimdi, önceki for
döngüsü örneklerini list comprehensions ile yeniden yazalım:
Örnek 1: Kareler Listesi
List comprehension ile kareler listesi
kareler_comp = [sayi ** 2 for sayi in range(10)]
print(f"Comprehension ile kareler: {kareler_comp}")
Çıktı: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Gördüğünüz gibi, 4 satırlık döngü kodu tek bir satıra indi. Okunuşu: “range(10) içindeki her sayi için sayi ** 2 ifadesinin sonucundan oluşan bir liste yap."
Örnek 2: Büyük Harf Listesi
isimler = ["alice", "bob", "charlie"]
List comprehension ile büyük harf listesi
buyuk_isimler_comp = [isim.upper() for isim in isimler]
print(f"Comprehension ile büyük isimler: {buyuk_isimler_comp}")
Çıktı: ['ALICE', 'BOB', 'CHARLIE']
Okunuşu: “isimler listesindeki her isim için isim.upper() ifadesinin sonucundan oluşan bir liste yap."
Bölüm 3: List Comprehensions’ın Avantajları
List comprehensions kullanmanın neden genellikle tercih edildiğine dair birkaç temel neden vardır:
Özlülük (Conciseness): En belirgin avantajı kodun daha kısa olmasıdır. Birçok durumda, 3–5 satırlık bir for
döngüsü bloğu yerine tek bir satır yeterli olur. Bu, özellikle basit liste oluşturma işlemleri için kodun daha az yer kaplamasını sağlar.
Okunabilirlik (Readability — Genellikle): Basit ve orta karmaşıklıktaki durumlar için, list comprehensions kodun amacını daha doğrudan ifade edebilir. [x*2 for x in numbers] ifadesi, "sayıların her birinin iki katını al" niyetini, bir for
döngüsü ve append
'den daha net bir şekilde gösterebilir. Ancak, çok karmaşık hale geldiklerinde okunabilirlik avantajı kaybolabilir (bu konuya daha sonra değineceğiz).
“Pythonic” Olması: List comprehensions, Python topluluğunda yaygın olarak kullanılan ve dilin ifade gücünü yansıtan “Pythonic” bir yapı olarak kabul edilir. Deneyimli Python geliştiricileri bu yapıyı sıkça kullanır ve bekler.
Potansiyel Performans Artışı: List comprehensions genellikle eşdeğer bir for
döngüsü ve append
çağrısından biraz daha hızlı çalışabilir. Bunun nedeni, döngü mekanizmasının ve liste oluşturma işleminin Python yorumlayıcısı tarafından daha optimize edilmiş bir şekilde (genellikle C seviyesine daha yakın) gerçekleştirilebilmesidir. append
metodunun her döngüde tekrar tekrar çağrılmasına gerek kalmaz. Ancak, bu performans farkı çoğu zaman çok büyük değildir ve okunabilirlik genellikle performanstan daha öncelikli olmalıdır.
Bölüm 4: List Comprehensions ile Koşullu Mantık
List comprehensions’ın gücü sadece basit dönüşümlerle sınırlı değildir. İçlerine koşullu mantık ekleyerek daha karmaşık listeler de oluşturabiliriz. İki temel koşullu yapı bulunur:
4.1. Filtreleme: if
Koşulu (Sonda)
Orijinal yinelenebilir nesnedeki öğeleri filtreleyerek sadece belirli bir koşulu sağlayan öğelerin işleme dahil edilmesini sağlayabiliriz. Bu durumda if koşulu, for döngüsünden sonra gelir.
[ifade for öğe in yinelenebilir if koşul]
Burada, koşul ifadesi her öğe için değerlendirilir. Sadece koşul True olduğunda ifade hesaplanır ve yeni listeye eklenir.
Örnek 1: Çift Sayıları Alma
Geleneksel döngü ile çift sayılar
cift_sayilar_dongu = []
for sayi in range(10):
if sayi % 2 == 0: # Filtreleme koşulu
cift_sayilar_dongu.append(sayi)
print(f"Döngü ile çift sayılar: {cift_sayilar_dongu}") # [0, 2, 4, 6, 8]
List comprehension ile çift sayılar
cift_sayilar_comp = [sayi for sayi in range(10) if sayi % 2 == 0]
print(f"Comprehension ile çift sayılar: {cift_sayilar_comp}") # [0, 2, 4, 6, 8]
Okunuşu: “range(10) içindeki her sayi için, eğer sayi % 2 == 0 ise, sayi'yı içeren bir liste yap."
Örnek 2: Belirli Uzunluktaki Kelimeler
kelimeler = ["python", "programlama", "dil", "list", "comprehension", "kısa", "kod"]
uzun_kelimeler = [kelime for kelime in kelimeler if len(kelime) > 5]
print(f"Uzunluğu 5'ten büyük kelimeler: {uzun_kelimeler}")
['python', 'programlama', 'comprehension']
4.2. Koşullu İfade: if-else
(İfade İçinde)
Bazen filtrelemek yerine, bir koşula bağlı olarak öğeye farklı işlemler uygulamak isteyebiliriz. Yani, “eğer koşul doğruysa X yap, değilse Y yap” gibi bir mantık kurmak isteriz. Bu durumda, Python’un koşullu ifadesi (ternary operator) ifade_dogru if koşul else ifade_yanlis yapısını, list comprehension'ın başındaki ifade kısmında kullanırız.
[ifade_dogru if koşul else ifade_yanlis for öğe in yinelenebilir]
Burada, for döngüsündeki her öğe için koşul değerlendirilir. Koşul True ise ifade_dogru hesaplanır ve listeye eklenir, False ise ifade_yanlis hesaplanır ve listeye eklenir. Burada tüm öğeler işleme dahil edilir, sadece uygulanan işlem koşula göre değişir.
Örnek 1: Sayıları ‘Çift’/’Tek’ Olarak Etiketleme
Geleneksel döngü ile
etiketler_dongu = []
for sayi in range(6):
if sayi % 2 == 0:
etiketler_dongu.append("Çift")
else:
etiketler_dongu.append("Tek")
print(f"Döngü ile etiketler: {etiketler_dongu}") # ['Çift', 'Tek', 'Çift', 'Tek', 'Çift', 'Tek']
List comprehension ile (if-else ifade içinde)
etiketler_comp = ["Çift" if sayi % 2 == 0 else "Tek" for sayi in range(6)]
print(f"Comprehension ile etiketler: {etiketler_comp}") # ['Çift', 'Tek', 'Çift', 'Tek', 'Çift', 'Tek']
Okunuşu: “range(6) içindeki her sayi için, eğer sayi % 2 == 0 ise 'Çift' değerini, değilse 'Tek' değerini içeren bir liste yap."
Dikkat: Koşullu ifadenin (if/else) yeri ile filtreleme if'inin yerinin farklı olduğuna dikkat edin. Filtreleme if'i for döngüsünden sonra gelirken, koşullu ifade if/else yapısı for döngüsünden önce, yani listenin öğelerini oluşturan ifadenin içinde yer alır.
Örnek 2: Negatif Sayıları 0 ile Değiştirme
sayilar = [10, -5, 20, 0, -15, 8]
pozitif_sayilar = [sayi if sayi >= 0 else 0 for sayi in sayilar]
print(f"Negatifler 0 ile değiştirildi: {pozitif_sayilar}") # [10, 0, 20, 0, 0, 8]
Bölüm 5: İç İçe List Comprehensions (Nested Comprehensions)
List comprehensions, iç içe geçmiş döngüler gerektiren durumları da modelleyebilir. Bu, özellikle matrisler (listelerin listesi) üzerinde çalışırken veya iç içe geçmiş veri yapılarından öğeleri çıkarırken kullanışlıdır.
İç içe geçmiş for
döngülerine karşılık gelen sözdizimi şöyledir:
[ifade for dışöğe in dış_yinelenebilir for içöğe in iç_yinelenebilir]
for döngülerinin sırası, geleneksel iç içe geçmiş döngülerdeki sırayla aynıdır (dıştaki döngü önce gelir).
Örnek 1: Matrisi Düzleştirme (Flattening a Matrix)
Bir listenin içindeki listelerin tüm öğelerini tek bir listede toplamak isteyelim.
matris = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
Geleneksel iç içe döngü ile
duz_liste_dongu = []
for satir in matris:
for eleman in satir:
duz_liste_dongu.append(eleman)
print(f"Döngü ile düzleştirilmiş liste: {duz_liste_dongu}") # [1, 2, 3, 4, 5, 6, 7, 8, 9]
İç içe list comprehension ile
duz_liste_comp = [eleman for satir in matris for eleman in satir]
print(f"Comprehension ile düzleştirilmiş liste: {duz_liste_comp}") # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Okunuşu: “matris içindeki her satir için, ve o satir içindeki her eleman için, eleman'ı içeren bir liste yap."
Örnek 2: İki Listenin Elemanlarının Kombinasyonları
harfler = ['a', 'b']
sayilar = [1, 2, 3]
İç içe list comprehension ile kombinasyonlar (tuple olarak)
kombinasyonlar = [(harf, sayi) for harf in harfler for sayi in sayilar]
print(f"Kombinasyonlar: {kombinasyonlar}")
[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3)]
Eşdeğer iç içe döngü:
kombinasyonlar_dongu = []
for harf in harfler:
for sayi in sayilar:
kombinasyonlar_dongu.append((harf, sayi))
İç İçe Comprehensions ve Okunabilirlik
Okunabilirlik Uyarısı: İç içe list comprehensions güçlü olsa da, ikiden fazla for
döngüsü veya karmaşık if
koşulları içerdiğinde hızla okunamaz hale gelebilirler. Eğer bir list comprehension çok karmaşıklaşıyorsa, okunabilirliği artırmak için geleneksel for
döngülerini kullanmak genellikle daha iyi bir seçenektir. Kodun amacı net olmalıdır.
Okunması Zor Olabilecek Bir Örnek (Kaçınılması Önerilir)
complex_comp = [x + y + z for x in range(5) if x % 2 == 0 for y in range(3) for z in range(2) if (x + y + z) % 3 == 0]
print(complex_comp)
Bunun yerine döngü kullanmak daha anlaşılır olabilir:
complex_loop = []
for x in range(5):
if x % 2 == 0:
for y in range(3):
for z in range(2):
toplam = x + y + z
if toplam % 3 == 0:
complex_loop.append(toplam)
print(f"Karmaşık işlem (döngü ile): {complex_loop}")
Bölüm 6: List Comprehensions vs. map()
ve filter()
Python’da listeler üzerinde dönüşüm ve filtreleme yapmak için map() ve filter() adında iki yerleşik fonksiyon daha bulunur. Bu fonksiyonlar genellikle lambda fonksiyonları ile birlikte kullanılır.
map(function, iterable): Verilen iterable
üzerindeki her öğeye function
'ı uygular ve sonuçları içeren bir map objesi (iterator) döndürür.
filter(function, iterable): Verilen iterable
üzerindeki öğelerden, function
uygulandığında True
döndürenleri içeren bir filter objesi (iterator) döndürür.
List comprehensions, birçok durumda map() ve filter() fonksiyonlarının yaptığı işi daha okunabilir ve doğrudan bir şekilde yapabilir.
Örnek: Sayıların Karesini Alma
sayilar = [1, 2, 3, 4]
map() ve lambda ile
kareler_map = list(map(lambda x: x**2, sayilar))
print(f"Kareler (map ile): {kareler_map}") # [1, 4, 9, 16]
List comprehension ile (genellikle daha okunabilir)
kareler_comp = [x**2 for x in sayilar]
print(f"Kareler (comp ile): {kareler_comp}") # [1, 4, 9, 16]
Örnek: Çift Sayıları Filtreleme
sayilar = [1, 2, 3, 4, 5, 6]
filter() ve lambda ile
ciftler_filter = list(filter(lambda x: x % 2 == 0, sayilar))
print(f"Çiftler (filter ile): {ciftler_filter}") # [2, 4, 6]
List comprehension ile (genellikle daha okunabilir)
ciftler_comp = [x for x in sayilar if x % 2 == 0]
print(f"Çiftler (comp ile): {ciftler_comp}") # [2, 4, 6]
Çoğu Python geliştiricisi, basit dönüşüm ve filtreleme işlemleri için list comprehensions’ı, map() ve filter()'a tercih eder çünkü genellikle daha açık ve daha az "dolambaçlı"dırlar (ekstra lambda
veya fonksiyon tanımına ihtiyaç duymazlar). Ancak, zaten var olan karmaşık bir fonksiyonu bir listedeki her öğeye uygulamak gerektiğinde map() hala kullanışlı olabilir.
Bölüm 7: Liste Anlayışlarının Ötesi: Set ve Dictionary Comprehensions
Comprehension yapısı sadece listelerle sınırlı değildir. Python, benzer bir sözdizimi ile set (küme) ve dictionary (sözlük) oluşturmaya da olanak tanır.
7.1. Set Comprehensions (Küme Üreteçleri)
List comprehension’a çok benzer, ancak köşeli parantez [] yerine süslü parantez {} kullanılır ve sonuç olarak benzersiz öğelerden oluşan bir set oluşturur.
Sözdizimi: {ifade for öğe in yinelenebilir if koşul}
sayilar = [1, 2, 2, 3, 1, 4, 5, 4]
Set comprehension ile benzersiz kareler
benzersiz_kareler = {x**2 for x in sayilar}
print(f"Benzersiz Kareler Kümesi: {benzersiz_kareler}") # {1, 4, 9, 16, 25}
Benzersiz çift sayılar
benzersiz_ciftler = {x for x in sayilar if x % 2 == 0}
print(f"Benzersiz Çiftler Kümesi: {benzersiz_ciftler}") # {2, 4}
7.2. Dictionary Comprehensions (Sözlük Üreteçleri)
Süslü parantez {} kullanılır, ancak ifade kısmı anahtar: değer şeklinde bir çift içerir. Sonuç olarak bir dictionary oluşturur.
Sözdizimi: {anahtar_ifadesi: deger_ifadesi for öğe in yinelenebilir if koşul}
Sayı: Kare eşleşmesi sözlüğü
kareler_dict = {x: x**2 for x in range(1, 6)}
print(f"Kareler Sözlüğü: {kareler_dict}")
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
İsim listesinden isim:uzunluk sözlüğü
isimler = ["elma", "armut", "kiraz"]
uzunluk_dict = {isim: len(isim) for isim in isimler}
print(f"Uzunluk Sözlüğü: {uzunluk_dict}")
{'elma': 4, 'armut': 5, 'kiraz': 5}
Mevcut sözlüğü filtreleyerek yeni sözlük oluşturma
fiyatlar = {"kalem": 2, "defter": 5, "silgi": 1, "cetvel": 3}
ucuz_urunler = {urun: fiyat for urun, fiyat in fiyatlar.items() if fiyat < 3}
print(f"Ucuz Ürünler Sözlüğü: {ucuz_urunler}")
{'kalem': 2, 'silgi': 1}
Set ve dictionary comprehensions da list comprehensions gibi okunabilirlik ve özlülük avantajları sunar.
Bölüm 8: Generator Expressions (Üreteç İfadeleri)
List comprehensions’a çok benzeyen ancak farklı bir amaca hizmet eden bir yapı da generator expressions (üreteç ifadeleri)’dır. Sözdizimi list comprehension ile neredeyse aynıdır, tek fark köşeli parantez [] yerine normal parantez () kullanılmasıdır.
Sözdizimi: (ifade for öğe in yinelenebilir if koşul)
Temel Fark: List comprehension, tüm listeyi bellekte hemen oluştururken, generator expression bir iterator (üreteç) nesnesi döndürür. Bu iterator, değerleri isteğe bağlı olarak, yani sadece ihtiyaç duyulduğunda (genellikle bir döngü içinde veya next() fonksiyonuyla) tek tek üretir. Buna lazy evaluation (tembel değerlendirme) denir.
Avantajları:
Bellek Verimliliği: Özellikle çok büyük veri dizileriyle çalışırken, tüm listeyi bellekte tutmak yerine değerleri tek tek ürettiği için çok daha az bellek kullanır.
Performans (Bazı Durumlarda): Eğer üretilen tüm değerlere aynı anda ihtiyaç yoksa (örneğin, sadece toplamını almak veya ilk uyan öğeyi bulmak gibi), tüm listeyi oluşturma maliyetinden kurtardığı için daha hızlı olabilir.
List comprehension (tüm liste bellekte oluşur)
milyon_kare_liste = [x**2 for x in range(1000000)]
print(milyon_kare_liste) # Çok büyük bir liste!
Generator expression (iterator nesnesi döner, değerler henüz üretilmedi)
milyon_kare_gen = (x**2 for x in range(1000000))
print(f"Generator Expression sonucu: {milyon_kare_gen}")
at 0x...>
Değerleri kullanmak için üzerinde iterasyon yapabiliriz
Örneğin, sadece ilk 5 değeri alalım:
print("İlk 5 kare (generator'dan):")
count = 0
for kare in milyon_kare_gen:
if count < 5:
print(kare)
count += 1
else:
break # Tümünü üretmeye gerek yok
Veya toplamını alalım (tüm liste belleğe alınmadan)
buyuk_sayi_kareleri_toplami = sum(x**2 for x in range(1000000))
print(f"\n1 Milyon sayının kareleri toplamı (generator ile hesaplandı).")
list() ile generator'ı listeye çevirebiliriz (ama o zaman bellek avantajı kaybolur)
liste_from_gen = list(milyon_kare_gen) # Dikkat: Generator tükenir!
Eğer sonucun bir liste olmasına ihtiyacınız varsa list comprehension kullanın. Eğer sadece üzerinde iterasyon yapacaksanız veya tek bir sonuç (toplam, maksimum vb.) hesaplayacaksanız ve veri büyükse, generator expression genellikle daha iyi bir seçenektir.
Bölüm 9: Sonuç ve En İyi Uygulamalar
Python list comprehensions, set comprehensions ve dictionary comprehensions, yinelenebilir nesnelerden yeni koleksiyonlar oluşturmak için güçlü, özlü ve sıklıkla okunabilir bir yol sunar. Geleneksel for
döngülerine kıyasla daha az kodla aynı işi yapabilirler ve potansiyel performans avantajları sağlayabilirler.
Ancak, bu gücü dikkatli kullanmak önemlidir:
Basit Tutun: Comprehension’lar basit dönüşümler ve filtrelemeler için harikadır. Çok fazla iç içe döngü veya karmaşık koşullar içerdiğinde okunabilirlik hızla düşer.
Okunabilirlik Önceliklidir: Eğer bir comprehension’ı anlamak zorlaşıyorsa, onu daha açık bir for
döngüsüne dönüştürmekten çekinmeyin. Kodun önce anlaşılır olması gerekir.
Yan Etkilerden Kaçının: Comprehension içindeki ifadenin (expression) sadece değer hesaplaması yapması, dış değişkenleri değiştirmemesi (yan etki yaratmaması) en iyi pratiktir. Yan etkili işlemler için for
döngüsü daha uygundur.
Doğru Aracı Seçin:
Sonucun bir liste olmasına ihtiyacınız varsa list comprehension kullanın.
Sonucun benzersiz öğelerden oluşan bir küme olması gerekiyorsa set comprehension kullanın.
Sonucun bir anahtar-değer eşlemesi (sözlük) olması gerekiyorsa dictionary comprehension kullanın.
Tüm listeyi bellekte tutmak istemiyorsanız veya çok büyük verilerle çalışıyorsanız generator expression’ı değerlendirin.
List comprehensions ve ilgili yapılar, Python’da akıcı ve etkili kod yazmanın önemli bir parçasıdır. Onları doğru bir şekilde anlamak ve yerinde kullanmak, Python becerilerinizi bir üst seviyeye taşıyacaktır.
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Kişisel WebSite
Abdulkadir Güngör - Özgeçmiş
Github
Github
Linkedin