← Kursa Dön
📄 Text · 15 min

Comprehension ve Generator Expressions

Comprehension, Python'un en sevilen ve en "Pythonic" özelliklerinden biri. For döngüsü ile 5-6 satırda yaptığın işi tek satırda, hem daha okunabilir hem daha hızlı yapmanı sağlar. Liste, dictionary, set ve generator — dört farklı comprehension türü var ve hepsini bu derste öğreneceksin.

Bir fabrika montaj hattı düşün: Hammaddeler giriyor (iterable), her biri bir işlemden geçiyor (expression), bazıları eleniyor (filter), ve sonuçta bitmiş ürünler çıkıyor (yeni koleksiyon). Comprehension tam olarak bu: [işlem(malzeme) for malzeme in hammaddeler if kalite_kontrol(malzeme)]. Tek satırda girdi → işlem → filtre → çıktı.


List Comprehension

Temel Söz Dizimi

# Söz dizimi: [ifade for değişken in iterable]
kareler = [x**2 for x in range(10)]
print(kareler)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Bu, şu for döngüsünün kısaltması:

kareler = []
for x in range(10):
    kareler.append(x**2)

Comprehension versiyonu:

  • Daha kısa (1 satır vs 3 satır)

  • Daha hızlı (C seviyesinde optimize edilmiş)

  • Daha okunabilir (amacı daha net ifade eder)

Koşullu (Filtreleme)

# [ifade for değişken in iterable if koşul]
cift_sayilar = [x for x in range(20) if x % 2 == 0]
print(cift_sayilar)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Pozitif sayıları al
sayilar = [-3, 5, -1, 8, -2, 7, 0]
pozitifler = [x for x in sayilar if x > 0]
print(pozitifler)  # [5, 8, 7]

if-else ile (Dönüşüm)

Dikkat: filtre (if sonda) ile dönüşüm (if-else başta) farklı yerlerde:

# Filtre: if sonda → elemanı dahil et veya etme
sadece_ciftler = [x for x in range(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]

# Dönüşüm: if-else başta → her eleman için karar ver
etiketler = ["çift" if x % 2 == 0 else "tek" for x in range(6)]
print(etiketler)  # ['çift', 'tek', 'çift', 'tek', 'çift', 'tek']

# Mutlak değer
mutlak = [x if x >= 0 else -x for x in [-3, 5, -1, 8, -2]]
print(mutlak)  # [3, 5, 1, 8, 2]

Kural: Filtre if → sona yaz. Dönüşüm if-else → başa (ifade kısmına) yaz.

String İşlemlerinde

isimler = ["  Ahmet ", "AYŞE", "mehmet", "  FaTmA  "]

# Temizle ve capitalize et
temiz = [isim.strip().capitalize() for isim in isimler]
print(temiz)  # ['Ahmet', 'Ayşe', 'Mehmet', 'Fatma']

# Sadece 5+ harfli isimleri al
uzun_isimler = [isim for isim in temiz if len(isim) >= 5]
print(uzun_isimler)  # ['Ahmet', 'Fatma']

# Her kelimeyi büyük harfe
metin = "python çok güzel bir dil"
kelimeler = [k.upper() for k in metin.split()]
print(kelimeler)  # ['PYTHON', 'ÇOK', 'GÜZEL', 'BİR', 'DİL']

Tip Dönüşümü

# String listesini int'e çevir
str_sayilar = ["1", "2", "3", "4", "5"]
int_sayilar = [int(s) for s in str_sayilar]
print(int_sayilar)  # [1, 2, 3, 4, 5]

# Geçersiz değerleri filtrele
veriler = ["42", "merhaba", "17", "", "99", "abc"]
gecerli = [int(v) for v in veriler if v.isdigit()]
print(gecerli)  # [42, 17, 99]

Fonksiyon Çağrısı ile

import math

sayilar = [1, 4, 9, 16, 25]
kokler = [math.sqrt(x) for x in sayilar]
print(kokler)  # [1.0, 2.0, 3.0, 4.0, 5.0]

# Lambda ile
carpim = [(lambda a, b: a * b)(x, x + 1) for x in range(5)]
print(carpim)  # [0, 2, 6, 12, 20]

Dict Comprehension

Sözlük oluşturmak için:

# Temel
kareler = {x: x**2 for x in range(1, 6)}
print(kareler)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Key-value ters çevirme
orijinal = {"a": 1, "b": 2, "c": 3}
ters = {v: k for k, v in orijinal.items()}
print(ters)  # {1: 'a', 2: 'b', 3: 'c'}

# İki listeden dict
isimler = ["Ahmet", "Ayşe", "Mehmet"]
notlar = [85, 92, 78]
sonuclar = {isim: not_ for isim, not_ in zip(isimler, notlar)}
print(sonuclar)

Koşullu Dict Comprehension

# Sadece geçen öğrenciler
notlar = {"Ahmet": 85, "Ayşe": 45, "Mehmet": 92, "Fatma": 55}
gecenler = {k: v for k, v in notlar.items() if v >= 60}
print(gecenler)  # {'Ahmet': 85, 'Mehmet': 92}

# Fiyat dönüşümü + filtre
fiyatlar_usd = {"laptop": 999, "mouse": 25, "keyboard": 75, "monitor": 350}
kur = 32.5
pahaliler_tl = {
    urun: fiyat * kur
    for urun, fiyat in fiyatlar_usd.items()
    if fiyat > 100
}
print(pahaliler_tl)
# {'laptop': 32467.5, 'monitor': 11375.0}

String İşlemlerinde

# Karakter frekansı
metin = "mississippi"
frekans = {harf: metin.count(harf) for harf in set(metin)}
print(frekans)  # {'m': 1, 's': 4, 'i': 4, 'p': 2}

# Kelime → uzunluk mapping
kelimeler = ["python", "java", "go", "rust", "javascript"]
uzunluklar = {k: len(k) for k in kelimeler}
print(uzunluklar)
# {'python': 6, 'java': 4, 'go': 2, 'rust': 4, 'javascript': 10}

# Environment variable parsing
env_str = "HOST=localhost;PORT=8080;DEBUG=true"
config = {
    k: v for k, v in
    (pair.split("=") for pair in env_str.split(";"))
}
print(config)  # {'HOST': 'localhost', 'PORT': '8080', 'DEBUG': 'true'}

Set Comprehension

Benzersiz elemanlardan oluşan set oluşturmak için:

# Temel
kareler = {x**2 for x in range(-5, 6)}
print(kareler)  # {0, 1, 4, 9, 16, 25}  (duplicate'lar otomatik temizlenir)

# Benzersiz kelime uzunlukları
metin = "the quick brown fox jumps over the lazy dog"
uzunluklar = {len(kelime) for kelime in metin.split()}
print(uzunluklar)  # {3, 4, 5}

# Bir metindeki benzersiz sesli harfler
sesli = {h for h in "programming".lower() if h in "aeiou"}
print(sesli)  # {'a', 'i', 'o'}

Set comprehension özellikle benzersiz değerler çıkarmak istediğinde kullanışlı. list(set(liste_comprehension)) yerine direkt set comprehension yaz.


Generator Expression: Lazy Evaluation

Generator expression, list comprehension'ın tembel (lazy) versiyonu. Tüm elemanları bellekte tutmaz — her istendiğinde bir sonrakini hesaplar.

# List comprehension — tüm elemanları bellekte tutar
liste = [x**2 for x in range(1_000_000)]  # ~8MB bellek

# Generator expression — neredeyse hiç bellek kullanmaz
gen = (x**2 for x in range(1_000_000))     # ~100 byte!

Söz Dizimi

# Köşeli parantez [] → liste
# Normal parantez () → generator

gen = (x**2 for x in range(5))
print(gen)  # <generator object <genexpr> at 0x...>
print(type(gen))  # <class 'generator'>

# next() ile tek tek al
print(next(gen))  # 0
print(next(gen))  # 1
print(next(gen))  # 4

# for ile tüket
for deger in gen:
    print(deger, end=" ")  # 9 16 (kalan elemanlar)

Ne Zaman Generator Kullan?

# ✅ sum, min, max gibi tek geçişli fonksiyonlarda
toplam = sum(x**2 for x in range(1_000_000))  # Parantez gereksiz!
print(toplam)

# ✅ any, all
hepsi_pozitif = all(x > 0 for x in [1, 2, 3, 4])
en_az_biri_cift = any(x % 2 == 0 for x in [1, 3, 4, 7])

# ✅ Zincirlemede (pipeline)
dosya_satirlari = (satir.strip() for satir in open("veri.txt"))
bos_olmayan = (satir for satir in dosya_satirlari if satir)
buyuk_harf = (satir.upper() for satir in bos_olmayan)

# Hiçbir adımda tüm dosya belleğe yüklenmez!
for satir in buyuk_harf:
    print(satir)

Bellek Karşılaştırması

import sys

# List — tüm sonuçları bellekte tutar
liste = [x**2 for x in range(10000)]
print(f"Liste: {sys.getsizeof(liste):,} byte")  # ~87,624 byte

# Generator — sadece formülü tutar
gen = (x**2 for x in range(10000))
print(f"Generator: {sys.getsizeof(gen)} byte")   # 200 byte!

Generator Tek Kullanımlık!

gen = (x for x in range(5))

# İlk kullanım — çalışır
print(list(gen))  # [0, 1, 2, 3, 4]

# İkinci kullanım — BOŞ!
print(list(gen))  # []

Generator bir kez tüketilince biter. Tekrar kullanmak istiyorsan yeniden oluştur veya listeye çevir.

💡 İpucu: Genel kural: sonucu birden fazla kullanacaksan list comprehension, tek geçiş yeterliyse veya veri çok büyükse generator expression kullan. sum(), max(), min(), any(), all(), "".join() gibi fonksiyonlara doğrudan generator ver — gereksiz liste oluşturma.


Nested Comprehension

İç İçe for (Düzleştirme)

# Matris → düz liste (flatten)
matris = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
duz = [eleman for satir in matris for eleman in satir]
print(duz)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Okuma sırası: soldan sağa, normal for döngüsü gibi:

# Eşdeğer for döngüsü
duz = []
for satir in matris:        # Dış for (ilk yazılan)
    for eleman in satir:    # İç for (sonra yazılan)
        duz.append(eleman)

Matris Transpoze

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

# Satır ↔ Sütun değiştir
transpose = [[satir[j] for satir in matris] for j in range(len(matris[0]))]
print(transpose)  # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

# zip ile daha temiz
transpose2 = [list(sutun) for sutun in zip(*matris)]
print(transpose2)  # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Karteziyen Çarpım

renkler = ["kırmızı", "mavi"]
bedenler = ["S", "M", "L"]

kombinasyonlar = [f"{renk}-{beden}" for renk in renkler for beden in bedenler]
print(kombinasyonlar)
# ['kırmızı-S', 'kırmızı-M', 'kırmızı-L', 'mavi-S', 'mavi-M', 'mavi-L']

Koşullu Nested Comprehension

# Matristeki çift sayıları topla
matris = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ciftler = [x for satir in matris for x in satir if x % 2 == 0]
print(ciftler)  # [2, 4, 6, 8]

# İç içe liste oluşturma
matris_yeni = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(matris_yeni)  # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

Nested Dict Comprehension

# Çarpım tablosu (dict of dicts)
carpim = {
    i: {j: i * j for j in range(1, 6)}
    for i in range(1, 6)
}
print(carpim[3][4])  # 12
print(carpim[5][5])  # 25

Walrus Operator (:=) Comprehension'da

Python 3.8'de gelen walrus operator, comprehension içinde hesaplama sonucunu hem kullanmanı hem atamanı sağlar:

# Hesapla, filtrele VE sonucu kullan
import math

sayilar = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# Karekökü hesapla, 5'ten büyükleri al
sonuclar = [
    kok
    for x in sayilar
    if (kok := math.sqrt(x)) > 5
]
print(sonuclar)  # [6.0, 7.0, 8.0, 9.0, 10.0]

Walrus olmadan aynı hesaplamayı iki kez yapmak gerekirdi:

# ❌ Hesaplama tekrarı
sonuclar = [math.sqrt(x) for x in sayilar if math.sqrt(x) > 5]

# ✅ Walrus ile tek hesaplama
sonuclar = [kok for x in sayilar if (kok := math.sqrt(x)) > 5]

Başka Örnekler

# Regex eşleşmelerini hem kontrol et hem al
import re

metinler = ["Python 3.12", "Java 21", "Go 1.21", "Rust 1.75"]
pattern = re.compile(r"(\w+)\s+([\d.]+)")

sonuclar = [
    (m.group(1), m.group(2))
    for metin in metinler
    if (m := pattern.match(metin))
]
print(sonuclar)
# [('Python', '3.12'), ('Java', '21'), ('Go', '1.21'), ('Rust', '1.75')]

# Pahalı fonksiyon sonucunu cache'le
def pahali_hesaplama(x):
    return x ** 3 + x ** 2 + x + 1

sonuclar = [
    (x, sonuc)
    for x in range(100)
    if (sonuc := pahali_hesaplama(x)) % 7 == 0
]
print(f"7'ye bölünebilen sonuç sayısı: {len(sonuclar)}")

Ne Zaman Comprehension, Ne Zaman For Döngüsü?

Comprehension Kullan ✅

  1. Basit dönüşüm: Bir koleksiyonu başka bir koleksiyona çevirme

  2. Filtreleme: Koşula göre eleman seçme

  3. Tek ifade: İşlem tek bir expression ile ifade edilebiliyorsa

  4. Yeni koleksiyon oluşturma: Sonuç bir liste/dict/set ise

# ✅ Basit dönüşüm
buyuk = [isim.upper() for isim in isimler]

# ✅ Filtreleme
pozitifler = [x for x in sayilar if x > 0]

# ✅ Mapping
fiyat_tl = {urun: fiyat * kur for urun, fiyat in fiyatlar.items()}

For Döngüsü Kullan ✅

  1. Yan etki (side effect): print, dosya yazma, API çağrısı

  2. Karmaşık mantık: Çoklu koşul, try-except, çoklu adım

  3. Accumulation: Toplam, sayaç gibi biriktirme işlemleri

  4. Okunabilirlik bozuluyorsa

# ✅ For döngüsü — yan etki var (print)
for isim in isimler:
    print(f"Merhaba, {isim}!")

# ❌ Comprehension ile yan etki — kötü pratik!
# [print(f"Merhaba, {isim}!") for isim in isimler]  # Gereksiz liste oluşturur

# ✅ For döngüsü — karmaşık mantık
sonuclar = []
for dosya in dosyalar:
    try:
        veri = dosya_oku(dosya)
        if veri:
            sonuclar.append(isleme(veri))
    except IOError:
        logla(f"Hata: {dosya}")

# ✅ For döngüsü — biriktirme
toplam = 0
for sayi in sayilar:
    toplam += sayi  # sum() daha iyi ama örnek olarak

⚠️ Dikkat — Aşırı Karmaşık Comprehension Anti-Pattern:

>

```python # ❌ BU NE?! Okunması imkansız result = [ transform(x, y) for x in range(10) if validate(x) for y in get_items(x) if check(x, y) and y not in seen and not (seen := seen | {y}) ]

>

# ✅ For döngüsü ile çok daha okunabilir result = [] seen = set() for x in range(10): if not validate(x): continue for y in get_items(x): if check(x, y) and y not in seen: seen.add(y) result.append(transform(x, y)) ```

>

Kural: Comprehension 1 satırda rahatça okunabiliyorsa kullan. 2-3 satıra yayılıyorsa belki tamam. Daha fazlaysa kesinlikle for döngüsüne geç.


Comprehension ile Yaygın Pattern'lar

Düzleştirme (Flatten)

# 2D → 1D
matris = [[1, 2], [3, 4], [5, 6]]
duz = [x for satir in matris for x in satir]
print(duz)  # [1, 2, 3, 4, 5, 6]

# Düzensiz iç içe listeler
duzensiz = [[1, 2], [3], [4, 5, 6], [7]]
duz = [x for alt_liste in duzensiz for x in alt_liste]
print(duz)  # [1, 2, 3, 4, 5, 6, 7]

Gruplama

# Çift ve tekleri ayır
sayilar = range(1, 11)
cift_tek = {
    "çift": [x for x in sayilar if x % 2 == 0],
    "tek": [x for x in sayilar if x % 2 != 0],
}
print(cift_tek)
# {'çift': [2, 4, 6, 8, 10], 'tek': [1, 3, 5, 7, 9]}

Zincirleme Dönüşüm (Pipeline)

# Ham veri → temiz, işlenmiş çıktı
ham_veriler = ["  42 ", "abc", "  17  ", "", "  99  ", "def", "  0  "]

# 1. Boşlukları temizle, 2. Sayı olmayanları filtrele, 3. int'e çevir, 4. Sıfırları atla
temiz = [
    int(v.strip())
    for v in ham_veriler
    if v.strip() and v.strip().lstrip("-").isdigit() and int(v.strip()) != 0
]
print(temiz)  # [42, 17, 99]

Arayüz Dönüşümü (API Response)

# API'den gelen ham veri
api_yaniti = [
    {"id": 1, "first_name": "John", "last_name": "Doe", "is_active": True},
    {"id": 2, "first_name": "Jane", "last_name": "Smith", "is_active": False},
    {"id": 3, "first_name": "Bob", "last_name": "Wilson", "is_active": True},
]

# İstediğimiz formata dönüştür
kullanicilar = [
    {
        "tam_isim": f"{k['first_name']} {k['last_name']}",
        "aktif": k["is_active"],
    }
    for k in api_yaniti
    if k["is_active"]  # Sadece aktif olanlar
]
print(kullanicilar)
# [{'tam_isim': 'John Doe', 'aktif': True}, {'tam_isim': 'Bob Wilson', 'aktif': True}]

Performans Karşılaştırması

import timeit

# List comprehension vs for + append
n = 1_000_000

# For döngüsü
def for_dongusu():
    sonuc = []
    for i in range(n):
        sonuc.append(i ** 2)
    return sonuc

# List comprehension
def comprehension():
    return [i ** 2 for i in range(n)]

# map
def map_yontemi():
    return list(map(lambda x: x ** 2, range(n)))

print(f"For döngüsü:  {timeit.timeit(for_dongusu, number=10):.3f}s")
print(f"Comprehension: {timeit.timeit(comprehension, number=10):.3f}s")
print(f"map:           {timeit.timeit(map_yontemi, number=10):.3f}s")

Tipik sonuçlar:

  • For döngüsü: ~1.5s

  • Comprehension: ~1.0s (~33% daha hızlı)

  • map: ~1.2s

Comprehension genellikle for döngüsünden %20-50 daha hızlı. Çünkü Python optimizer'ı comprehension'ları daha verimli bytecode'a çevirir.


Pratik Örnekler

Örnek 1: CSV Benzeri Veri İşleme

ham_veri = [
    "Ahmet,25,İstanbul,85",
    "Ayşe,22,Ankara,92",
    "Mehmet,30,İzmir,78",
    "",  # Boş satır
    "Fatma,28,Bursa,88",
]

# Parse et, boş satırları atla
ogrenciler = [
    dict(zip(["isim", "yas", "sehir", "not"], satir.split(",")))
    for satir in ham_veri
    if satir.strip()
]

# 80+ not alanları filtrele
basarili = [
    o["isim"]
    for o in ogrenciler
    if int(o["not"]) >= 80
]
print(basarili)  # ['Ahmet', 'Ayşe', 'Fatma']

Örnek 2: Matris İşlemleri

# İki matrisin elemanları toplamı
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [[9, 8, 7], [6, 5, 4], [3, 2, 1]]

C = [
    [A[i][j] + B[i][j] for j in range(len(A[0]))]
    for i in range(len(A))
]
print(C)  # [[10, 10, 10], [10, 10, 10], [10, 10, 10]]

Örnek 3: Kelime İndeksi

metin = "python güzel python harika güzel bir dil python harika"
kelimeler = metin.split()

# Her kelimenin geçtiği indeksler
indeks = {
    kelime: [i for i, k in enumerate(kelimeler) if k == kelime]
    for kelime in set(kelimeler)
}
print(indeks)
# {'python': [0, 2, 8], 'güzel': [1, 4], 'harika': [3, 7], ...}

Örnek 4: Sınıf Verisi Dönüştürme

from collections import namedtuple

Ogrenci = namedtuple("Ogrenci", "isim yas not_")

ham = [("Ahmet", "25", "85"), ("Ayşe", "22", "92"), ("Mehmet", "30", "78")]

# Tuple → NamedTuple, string → int dönüşümü
ogrenciler = [
    Ogrenci(isim, int(yas), int(not_))
    for isim, yas, not_ in ham
]

# Dict'e çevir
ogrenci_dict = {o.isim: o._asdict() for o in ogrenciler}
print(ogrenci_dict)

Özet

  • List comprehension [ifade for x in iterable if koşul] — for döngüsünden %20-50 daha hızlı ve daha okunabilir.

  • Dict comprehension {k: v for k, v in ...} — sözlük oluşturma, dönüştürme ve filtreleme için ideal.

  • Set comprehension {ifade for x in iterable} — benzersiz elemanlar kümesi oluşturmak için kullan.

  • Generator expression (ifade for x in iterable) — lazy evaluation ile bellek dostu; sum(), any(), all() gibi tek geçişli fonksiyonlara doğrudan ver.

  • Nested comprehension matris düzleştirme ve transpoze için kullanışlı — okuma sırası soldan sağa, normal for gibi.

  • Okunabilirlik kuralı: Comprehension 1-2 satırda rahat okunabiliyorsa kullan; karmaşıklaşıyorsa veya yan etki varsa for döngüsüne geç.