Ders 10 / 16
Kavrama Sözdizimi
Aynı bin öğeden liste kavraması 1000, küme ve sözlük kavraması 100 öğe tutar; süzme koşulunun üretimden önce mi sonra mı yazıldığı var edilen nesneyi 5'ten 10'a çıkarır.
İçindekiler
Önceki iki ders köşeli ayraç ile parantez arasındaki farkı ölçtü ve tembelliğin neyi azaltıp neyi azaltmadığını sayıyla ayırdı. Ama köşeli ayracın kendisi hiç açılmadı: kavrama her ölçümde yalnız “hepsini şimdi üreten biçim” olarak durdu.
Oysa aynı yazım üç ayrı kap kurabilir. Köşeli ayraç liste, küme ayracı küme, aynı ayraç içinde iki nokta üst üste ile yazılan çift sözlük üretir. Üçü de aynı kaynaktan aynı öğeleri alır, ama üçü aynı sayıda öğe tutmaz. Bu dersin sorduğu iki soru şudur: üç kavrama biçimi aynı bin öğeden kaçını tutar, ve süzme koşulunun nereye yazıldığı var edilen nesne sayısını değiştirir mi?
Üç Kap, Tek Yazım
Kavrama bir döngü ile bir kap kurulumunun tek deyime sıkıştırılmış hâlidir. Üç biçimin sözdizimi neredeyse aynıdır ve aralarındaki tek fark ayraç ile öğe ifadesidir. Kavrama yazımı üreteç ifadesinden yalnız ayraçla ayrılır ve bu tek karakter kurulum davranışını tamamen değiştirir; önceki ders bunu ölçtü.
Üç kabın ayrıldığı yer öğeyi nasıl kabul ettikleridir. Liste her öğeyi sırayla kabul eder ve hiçbirini elemez. Küme öğeyi karma değerine göre yerleştirir ve eşiti varsa yenisini almaz. Sözlük aynı işi anahtar üzerinde yapar, ama değeri değiştirir: aynı anahtar ikinci kez geldiğinde eski değer düşer.
Veri Yapıları kursu bu üç kabın mekanizmasını — karma tablosu, çakışma, yerleşim — kurmuştu; burada mekanizma tekrarlanmaz. Ölçülen şey aynı kaynağın üç kapta kaç öğeye indiğidir.
Koşulun İki Yeri
Kavrama iki ayrı yerde koşul kabul eder ve ikisi aynı şey değildir.
Süzme koşulu yazımın sonuna gelir ve döngü değişkeni üzerinde çalışır: koşulu geçmeyen tur öğe ifadesini hiç çalıştırmaz. Koşullu ifade ise öğe ifadesinin kendi içindedir ve her turda bir sonuç seçer; hiçbir turu elemez.
İkisi karıştırıldığında sonuç aynı görünebilir ama sayı görünmez. Ölçüm bunu ayırır.
Ölçümün varsayımları:
- YN28 — Ortak tanımın
Ogesınıfı sayma düzeniyle aynen kullanılır; kâhin kurgunun kendisidir. - YN29 — Üç kavrama aynı bin öğelik kaynak üzerinde çalışır ve kaynak ölçümden önce kurulmuştur; bu yüzden üçünün de “var edilen” sütunu sıfırdır. Ölçülen şey tutmadır.
- YN30 — Küme ve sözlük kavramaları öğeleri
deger % 100üzerinden gruplar; sayı keyfîdir ve yalnız çakışmanın gözlenebilmesi için seçilmiştir. - YN31 — “Paylaşılan Oge”, kabın içinde kaynaktaki nesnelerin kendisinin durup durmadığıdır; türetilmiş bir sayı tutan kapta bu sütun sıfırdır.
- YN32 — Koşul ölçümü on öğede yapılır ve koşul öğelerin yarısını geçirir; ölçülen şey büyüklük değil, iki yazım arasındaki üretim farkıdır.
- YN33 — İç içe ölçümde tablo dört satır, üç sütundur; toplam öğe üç yazımda da 12’dir. “Kurulan kap” sayısına dış kap da dahildir.
- YN34 — Kapsam ölçümünde kavramanın döngü adı, dışarıda aynı adı taşıyan bir değişkenle kasıtlı olarak çakıştırılır.
- YN35 — Son ölçümde iki yazım birebir aynıdır; değişen tek şey iç kaynağın liste mi üreteç ifadesi mi olduğudur. Dış döngü üç turludur ve iç kaynak iki öğelidir.
Ölçüm
"""Kavrama: uc kap, ayni kaynak, ayri tutma; ve icice kavramanin ara nesnesi.""" SAYAC = {"uretilen": 0} class Oge: """Her uretimi sayan oge.""" def __init__(self, deger): SAYAC["uretilen"] += 1 self.deger = deger def __repr__(self): return f"Oge({self.deger})" def sifirla(): SAYAC["uretilen"] = 0 def uretilen(): return SAYAC["uretilen"] N = 1000 KAYNAK = [Oge(i) for i in range(N)] KAVRAMALAR = ( ("liste kavraması", lambda: [o for o in KAYNAK]), ("küme kavraması", lambda: {o.deger % 100 for o in KAYNAK}), ("sözlük kavraması", lambda: {o.deger % 100: o for o in KAYNAK})) print(f"{'kavrama':<18s} {'var edilen':>11s} {'kapta tutulan':>14s}" f" {'paylaşılan Oge':>15s}") for ad, kur in KAVRAMALAR: sifirla() kap = kur() ogeler = list(kap.values()) if isinstance(kap, dict) else list(kap) ortak = sum(1 for o in ogeler if isinstance(o, Oge)) print(f" {ad:<16s} {uretilen():11d} {len(kap):14d} {ortak:15d}") def kosul_once(n=10): sifirla() liste = [Oge(i) for i in range(n) if i % 2 == 0] return uretilen(), len(liste) def kosul_sonra(n=10): sifirla() liste = [o for o in (Oge(i) for i in range(n)) if o.deger % 2 == 0] return uretilen(), len(liste) def kosullu_ifade(n=10): sifirla() liste = [Oge(i) if i % 2 == 0 else Oge(-i) for i in range(n)] return uretilen(), len(liste) print() print(f"{'koşulun yeri':<28s} {'var edilen':>11s} {'tutulan':>8s}") for ad, f in (("üretimden önce süzme", kosul_once), ("üretimden sonra süzme", kosul_sonra), ("koşullu ifade (süzme yok)", kosullu_ifade)): u, t = f() print(f" {ad:<26s} {u:11d} {t:8d}") def ic_ice(m=4, k=3): sifirla() tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)] return uretilen(), 1 + len(tablo), sum(len(s) for s in tablo) def duz(m=4, k=3): sifirla() liste = [Oge(i * k + j) for i in range(m) for j in range(k)] return uretilen(), 1, len(liste) def duzlestirme(m=4, k=3): sifirla() tablo = [[Oge(i * k + j) for j in range(k)] for i in range(m)] liste = [o for satir in tablo for o in satir] return uretilen(), 2 + len(tablo), len(liste) print() print(f"{'iç içe yazım':<24s} {'var edilen':>11s} {'kurulan kap':>12s}" f" {'öğe':>4s}") for ad, f in (("satır satır tablo", ic_ice), ("tek düzeyde kavrama", duz), ("tabloyu düzleştirme", duzlestirme)): u, kap, oge = f() print(f" {ad:<22s} {u:11d} {kap:12d} {oge:4d}") i = "dışarıdaki değer" kareler = [i * i for i in range(4)] print() print(f"kavramadan sonra dışarıdaki ad: {i!r}, sonuç {kareler}") sifirla() sozluk = {o.deger: o for o in (Oge(i % 4) for i in range(12))} print(f"anahtar çakışması: üretilen {uretilen()}, tutulan {len(sozluk)}," f" son değer kazandı -> {sozluk[0].deger}") def ic_kaynak_liste(): sifirla() ic = [0, 1] liste = [Oge(i * 2 + j) for i in range(3) for j in ic] return uretilen(), len(liste) def ic_kaynak_akis(): sifirla() ic = (x for x in [0, 1]) liste = [Oge(i * 2 + j) for i in range(3) for j in ic] return uretilen(), len(liste) print() print(f"{'iç kaynağın türü':<28s} {'var edilen':>11s} {'tutulan':>8s}") for ad, f in (("liste (yeniden gezilebilir)", ic_kaynak_liste), ("üreteç ifadesi (tek geçişlik)", ic_kaynak_akis)): u, t = f() print(f" {ad:<30s} {u:9d} {t:8d}")
kavrama var edilen kapta tutulan paylaşılan Oge liste kavraması 0 1000 1000 küme kavraması 0 100 0 sözlük kavraması 0 100 100 koşulun yeri var edilen tutulan üretimden önce süzme 5 5 üretimden sonra süzme 10 5 koşullu ifade (süzme yok) 10 10 iç içe yazım var edilen kurulan kap öğe satır satır tablo 12 5 12 tek düzeyde kavrama 12 1 12 tabloyu düzleştirme 12 6 12 kavramadan sonra dışarıdaki ad: 'dışarıdaki değer', sonuç [0, 1, 4, 9] anahtar çakışması: üretilen 12, tutulan 4, son değer kazandı -> 0 iç kaynağın türü var edilen tutulan liste (yeniden gezilebilir) 6 6 üreteç ifadesi (tek geçişlik) 2 2
Üç Kabın Tuttuğu
Üst tabloda “var edilen” sütunu üç satırda da 0. Bu bir tuhaflık değil, kurgunun
kendisidir: bin öğe ölçümden önce kurulmuştu ve üç kavrama da yeni bir Oge üretmiyor,
yalnız var olanları alıyor. Ölçülen şey burada üretim değil, tutma.
Liste kavraması 1000 öğe tutuyor ve bunların 1000’i kaynaktaki nesnenin kendisi. Yeni bir kap kuruldu, içine bin başvuru yazıldı, hiçbir öğe çoğaltılmadı — ilk dersin ölçtüğü kap kurma işleminin ta kendisi.
Küme kavraması 100 öğe tutuyor ve paylaşılan 0. İki sayı birlikte okunmalı:
kavramanın öğe ifadesi o.deger % 100 bir sayı üretiyor, Oge değil; kap kaynaktaki
nesneleri hiç almıyor. Bin turdan yalnız yüz ayrı değer çıkıyor, çünkü küme eşitini ikinci
kez kabul etmiyor. Bin öğe okundu, yüz öğe tutuldu — ve okunan ile tutulan arasındaki fark
900.
Sözlük kavraması da 100 tutuyor ama paylaşılan 100. Anahtar türetilmiş bir sayı,
değer ise kaynaktaki nesnenin kendisi. Yani sözlük hem eledi hem paylaştı: yüz anahtarın
her biri, o anahtara düşen son Oge nesnesine bağlı.
Son satır bunun sonucunu doğrudan gösteriyor. On iki öğe üretildi, dördü tutuldu ve çakışan anahtarlarda son değer kazandı. Sözlük kavramasında elemeyi yapan şey sessizdir: düşen öğeler için ne uyarı vardır ne istisna. Bin satırlık bir kaynaktan yüz satırlık bir sözlük çıktığında yazım aynı kalır, kaybın izi ancak sayılırsa görünür.
Koşul Nereye Yazılırsa
Orta tablo üç satırında da aynı on turu döner ama üretim sayıları ayrılır.
Üretimden önce süzme — koşul yazımın sonunda ve döngü değişkeni üzerinde — 5 nesne
var ediyor. Koşulu geçmeyen beş tur öğe ifadesine hiç ulaşmıyor; Oge çağrısı yapılmıyor.
Üretimden sonra süzme aynı beş öğeyi tutuyor ama 10 nesne var ediyor. Burada koşul öğenin kendisine bakıyor, dolayısıyla öğenin önce var olması gerekiyor. Beş nesne doğuyor, süzülüyor ve hiçbir kaba girmediği için erişilemez oluyor.
İki satırın tuttuğu sayı aynı: 5. Sonuç da aynı. Ayrılan tek şey elemenin hangi tarafta yapıldığı. Süzme koşulu kaynağın diliyle yazılabiliyorsa üretimden kaçınılır; yalnız üretilmiş nesnenin özelliğine bakılabiliyorsa kaçınılamaz. Bu, önceki dersin erken bırakma ölçümünün kavrama içindeki karşılığıdır — tembellik yine bir olanaktır, koşulun nereye yazılabildiğine bağlı.
Koşullu ifade satırı üçüncü davranışı gösteriyor: 10 üretim, 10 tutma. Buradaki
if bir süzgeç değil, bir seçicidir; hiçbir turu elemez, yalnız hangi nesnenin
üretileceğini seçer. İki yazım birbirine benzer ve konumları farklıdır: seçici öğe
ifadesinin içinde, süzgeç yazımın sonundadır. Ayrımı sayı verir — süzgeç tutulanı azaltır,
seçici azaltmaz.
İç İçe Kavramanın Ara Kapları
Alt tablo aynı on iki öğeyi üç yazımla kuruyor. “Var edilen” sütunu üçünde de 12 ve öğe sayısı üçünde de 12; ayrılan sütun kap sayısıdır.
Satır satır tablo — bir kavramanın içinde başka bir kavrama — 5 kap kuruyor: dört satır listesi ve onları taşıyan dış liste. İç kavrama her dış turda yeniden çalışıyor ve her seferinde yeni bir liste üretiyor. Bu listeler ara nesne değil, sonucun kendisidir; tablo yapısı isteniyorsa gereklidir.
Tek düzeyde kavrama — iki for yan yana, tek öğe ifadesi — aynı on iki öğeyi 1 kap
kuruyor. İki döngü tek kavrama içinde iç içe dönüyor ve ara liste hiç doğmuyor. Sıra da
aynı: soldaki for dış döngüdür.
Tabloyu düzleştirme üçüncü yolu gösteriyor: önce tablo kurulur, sonra düzleştirilir. Sonuç ikinci satırla aynı, kap sayısı 6. Beş kap kuruldu, biri daha eklendi ve kurulan dört satır listesi düzleştirmeden sonra hiçbir işe yaramıyor — bunlar gerçek ara nesnelerdir. Aynı sonucun tek kapla kurulabildiği yerde beşi fazladan.
Ayrım şuraya oturur: iç içe kavrama yapıyı kurar, yan yana for geziyi kurar. Sonuç
düz bir dizi olacaksa yapıyı kurup sonra yıkmak, hiç kurmamaktan pahalıdır.
İç Kaynak Kaç Kez Gezilir
Son tablo yan yana iki for yazımının en kolay gözden kaçan yanını ölçüyor: iki yazım
birebir aynı, sonuçlar 6’ya karşı 2.
Fark iç kaynağın türünden geliyor. Sağdaki for her dış turda yeniden gezilir; üç dış
tur için iç kaynaktan üç kez baştan geçilmesi gerekir. İç kaynak bir listeyse bu sorunsuz
olur — liste her istendiğinde taze bir yineleyici verir ve üç kez ikişer öğe, toplam 6
öğe çıkar.
İç kaynak bir üreteç ifadesiyse olmaz. İlk dış turda akış tüketilir; ikinci ve üçüncü turlarda aynı akıştan yeniden geçilmek istenir ve akış boş döner. Sonuç 2 öğe, ve ortada ne istisna vardır ne uyarı. Birinci derste ölçülen tek geçişlilik burada bir sonuç üretiyor: tükenmiş akış boş akıştan ayırt edilemediği için kavrama sessizce eksik bir liste kuruyor.
Bu, üç dersin biriktirdiği ayrımın pratikteki karşılığıdır. Bir akış yeniden gezilecek bir yere konuyorsa — iç içe bir döngünün iç kaynağı, iki kez okunacak bir değişken — önce listeye alınmalıdır. Alınmazsa ikinci geçiş hata değil, sessiz bir eksik üretir.
Kavramanın Kendi Kapsamı
Son iki satır Python’a özgü bir ayrıntıyı ölçüyor. Kavramanın döngü değişkeni dışarıdaki
aynı adlı değişkeni ezmiyor: kavramadan sonra i hâlâ kendi değerini taşıyor, kavrama
ise kendi i adıyla [0, 1, 4, 9] üretiyor.
Bunun nedeni kavramanın kendi kapsamına sahip olmasıdır. Python Temelleri kursunun kapsam dersi adın hangi düzeyde arandığını kurmuştu; burada eklenen tek şey kavramanın bu düzeylerden birini kendisi açtığıdır. Döngü adı kavramanın içinde doğar ve orada ölür.
Sıradan bir for döngüsünde durum böyle değildir: orada döngü değişkeni çevreleyen kapsama
yazılır ve döngü bittikten sonra da yaşar. Aynı işi yapan iki yazımdan biri ad bırakıyor,
öbürü bırakmıyor. Bu, kavramanın yalnız bir kısaltma olmadığını gösterir — ad görünürlüğü
bakımından ayrı bir düzenektir.
Özet
- Aynı bin öğelik kaynaktan liste kavraması 1000, küme ve sözlük kavraması 100 öğe
tutar; paylaşılan
Ogesayısı sırasıyla 1000, 0 ve 100’dür. Kap ne kadarını saklayacağına kendisi karar verir ve eleme sessizdir. - Süzme koşulu üretimden önce yazılabiliyorsa var edilen 5’te kalır; yalnız üretilmiş nesneye bakılabiliyorsa 10 olur. Tutulan iki yazımda da 5’tir.
- Öğe ifadesinin içindeki koşullu ifade bir süzgeç değildir: hiçbir turu elemez, 10 üretir ve 10 tutar.
- İç içe kavrama on iki öğe için 5 kap kurar, yan yana iki
foraynı öğeleri 1 kapla verir; önce kurup sonra düzleştirmek 6 kap eder ve dördü ara nesnedir. - Kavramanın kendi kapsamı vardır: döngü adı dışarıdaki aynı adlı değişkeni ezmez ve
kavrama bitince kaybolur; sıradan
fordöngüsünde ad geride kalır. - Yan yana iki
foryazımında sağdaki kaynak her dış turda yeniden gezilir: liste 6 öğe verir, aynı yazımda üreteç ifadesi 2. Tükenmiş akış sessizce boş davrandığı için eksik sonuç istisna üretmez.
Sonraki Adım
Bu derste süzme koşulu da öğe ifadesi de kavramanın içine yazıldı; her ikisi de yazıldığı yere gömülü kaldı ve başka bir kavramada yeniden kullanılamadı. Oysa ikisi de bir kural ifade ediyor — “şunu geçir”, “şunu şuna dönüştür” — ve kurallar adlandırılabilir. Sonraki ders kuralı bir nesne olarak ele alır: bir işlev bir başka işleve argüman olarak geçirilebilir mi, ve geçirildiğinde kaç kez çağrılır? Sıralamada anahtar hesabı öğe başına bir kez mi yapılır, yoksa her karşılaştırmada yeniden mi?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.