Kayıtlar ve iç içe veri yapıları

Programlama Temelleri dersinde liste içinde sözlük gibi iç içe yapılarla tanıştınız. Bu hafta aynı sözdizimini yeniden öğrenmeyeceğiz. Asıl sorumuz şudur:

Aynı kayıt kümesini farklı biçimlerde düzenlersek hangi işlemler kolaylaşır, hangileri zorlaşır?

Bu derste henüz sınıf tasarımı yapmıyoruz. Bu nedenle kayıtları çoğunlukla sözlüklerle temsil edeceğiz; ancak kayıtların hangi dış yapı içinde tutulduğu problem gereksinimine göre değişecektir.

1 Bu hafta neleri yapabilmelisiniz?

Bölümün sonunda:

  • bir kaydı sözlükle temsil edebilmeli,
  • çok sayıda kaydı liste içinde sözlüklerle düzenleyebilmeli,
  • aynı kayıtları kimliğe göre indeksleyen bir dict görünümü oluşturabilmeli,
  • gruplama için dict + list yapısını kullanabilmeli,
  • kayıt ekleme, güncelleme, filtreleme ve özetleme işlemleri gerçekleştirebilmeli,
  • aynı verinin farklı temsil biçimlerinin hangi işlemleri kolaylaştırdığını açıklayabilmeli,
  • sığ kopya ve ortak referans davranışını iç içe yapılarda fark edebilmelisiniz.

2 Bir kayıt: alan adları neden önemlidir?

Bir öğrenciyi tek tek değişkenlerle tutabiliriz:

number = 102
name = "Bora"
department = "Yazılım"
grade = 78

Fakat bu değerlerin aynı öğrenciye ait olduğu yapısal olarak görünmez. Sözlük bu ilişkiyi daha açık hâle getirir:

#| edit: false
#| completion: false
student = {
    "number": 102,
    "name": "Bora",
    "department": "Yazılım",
    "grade": 78,
}

print(student["name"])
print(student["grade"])

Burada sözlük, alan adlarını doğrudan görünür kılar.

3 Temsil 1 — liste içinde kayıtlar

Bir sınıftaki öğrencileri liste içinde sözlüklerle tutabiliriz:

#| edit: false
#| completion: false
students = [
    {"number": 101, "name": "Ayşe", "department": "Yazılım", "grade": 86},
    {"number": 102, "name": "Bora", "department": "Siber", "grade": 72},
    {"number": 103, "name": "Cem", "department": "Yazılım", "grade": 91},
]

for student in students:
    print(student["number"], student["name"])

Bu temsil özellikle şu işlemler için doğaldır:

  • kayıtların sırasını koruma,
  • bütün kayıtları dolaşma,
  • filtreleme,
  • sıralı rapor üretme.

flowchart TD
    A["students: list"] --> B["record 1: dict"]
    A --> C["record 2: dict"]
    A --> D["record 3: dict"]
    B --> B1["number"]
    B --> B2["name"]
    B --> B3["grade"]

4 Liste üzerinde kimliğe göre arama

Öğrenci numarasına göre kayıt bulmak için listeyi dolaşabiliriz:

#| edit: false
#| completion: false
def find_by_number(students, number):
    for student in students:
        if student["number"] == number:
            return student
    return None

students = [
    {"number": 101, "name": "Ayşe", "grade": 86},
    {"number": 102, "name": "Bora", "grade": 72},
    {"number": 103, "name": "Cem", "grade": 91},
]

print(find_by_number(students, 102))

Bu çözüm yanlış değildir. Fakat aynı tür arama binlerce kez yapılacaksa her seferinde listeyi dolaşmak daha az uygun olabilir.

5 Temsil 2 — kimliğe göre indekslenmiş sözlük

Aynı kayıtları numaradan kayda erişecek biçimde düzenleyebiliriz:

#| edit: false
#| completion: false
students_by_number = {
    101: {"name": "Ayşe", "department": "Yazılım", "grade": 86},
    102: {"name": "Bora", "department": "Siber", "grade": 72},
    103: {"name": "Cem", "department": "Yazılım", "grade": 91},
}

print(students_by_number[102])

Bu temsil, “102 numaralı öğrenci kim?” sorusunu doğrudan ifade eder.

5.1 Aynı veri, farklı güçlü taraf

Temsil Güçlü olduğu işlem
list[dict] sırayla dolaşma, filtreleme, raporlama
dict[id -> record] kimliğe göre sık erişim
Important

Bir temsilin daha iyi olması problemden bağımsız değildir. En sık yapılan işlem, dış veri yapısının seçimini etkiler.

6 Temsil 3 — gruplama: dict + list

Bazen anahtardan tek bir kayda değil, bir kayıt grubuna ulaşmak isteriz. Örneğin öğrencileri bölüme göre gruplayalım:

#| edit: false
#| completion: false
students = [
    {"name": "Ayşe", "department": "Yazılım"},
    {"name": "Bora", "department": "Siber"},
    {"name": "Cem", "department": "Yazılım"},
    {"name": "Deniz", "department": "Siber"},
]

groups = {}

for student in students:
    department = student["department"]
    if department not in groups:
        groups[department] = []
    groups[department].append(student)

for department, members in groups.items():
    print(department, "->", [member["name"] for member in members])

Burada dış sözlüğün her anahtarı bir grubu, değer olarak tutulan liste ise o grubun üyelerini temsil eder.

7 Üç temsili birlikte görelim

flowchart LR
    R["Aynı kayıt kümesi"] --> L["list[dict]"]
    R --> I["dict[id → record]"]
    R --> G["dict[group → list[record]]"]
    L --> L1["sıra / filtre / rapor"]
    I --> I1["kimliğe göre erişim"]
    G --> G1["gruba göre erişim"]

Gerçek programlarda bu görünümlerden birden fazlası aynı anda bulunabilir.

8 Aynı kayıtların birden çok yapıdan görülmesi

Bir listedeki kayıtları indeksleyen sözlüğü sonradan oluşturabiliriz:

#| edit: false
#| completion: false
students = [
    {"number": 101, "name": "Ayşe", "grade": 86},
    {"number": 102, "name": "Bora", "grade": 72},
]

by_number = {}
for student in students:
    by_number[student["number"]] = student

by_number[102]["grade"] = 80

print(students)
print(by_number[102])

Neden listedeki kayıt da değişti? Çünkü students listesi ile by_number sözlüğü aynı öğrenci sözlüğü nesnesine referans veriyor.

Bu davranış yararlı olabilir: tek kaydı iki ayrı yerde kopyalamadan iki farklı erişim biçimi oluştururuz. Ancak bağımsız kopya bekliyorsak hata kaynağı olabilir.

9 Filtreleme kalıbı

Belirli koşula uyan kayıtları yeni bir listede toplayalım:

#| edit: false
#| completion: false
students = [
    {"name": "Ayşe", "department": "Yazılım", "grade": 86},
    {"name": "Bora", "department": "Siber", "grade": 72},
    {"name": "Cem", "department": "Yazılım", "grade": 91},
    {"name": "Deniz", "department": "Siber", "grade": 64},
]

selected = []
for student in students:
    if student["department"] == "Yazılım" and student["grade"] >= 80:
        selected.append(student)

for student in selected:
    print(student["name"], student["grade"])

Filtrelemede bütün kayıtları görmek gerektiği için list[dict] temsili doğal bir seçimdir.

10 Alıştırma — düşük stoklu ürünler

stock değeri 10’dan küçük ürünlerin adlarını low_stock listesine ekleyin.

#| exercise: hafta04-filtre
#| completion: false
#| persist: true
products = [
    {"code": "K1", "name": "Kalem", "stock": 18},
    {"code": "D2", "name": "Defter", "stock": 6},
    {"code": "S3", "name": "Silgi", "stock": 9},
    {"code": "C4", "name": "Cetvel", "stock": 15},
]

low_stock = []

for product in products:
    # Burayı tamamlayın.
    pass

print(low_stock)

Beklenen sonuç:

['Defter', 'Silgi']
#| exercise: hafta04-filtre
#| check: true
scope = {}
try:
    exec(user_code, scope)
    correct = scope.get("low_stock") == ["Defter", "Silgi"]
except Exception:
    correct = False

feedback = (
    {"correct": True, "message": "Düşük stoklu ürünler doğru filtrelendi."}
    if correct
    else {"correct": False, "message": "Her ürün için stock < 10 koşulunu kontrol edip product['name'] değerini ekleyin."}
)
feedback

if product["stock"] < 10: koşulundan sonra low_stock.append(...) kullanın.

Solution.

for product in products:
    if product["stock"] < 10:
        low_stock.append(product["name"])

11 Alıştırma — kimliğe göre indeks oluştur

Aşağıdaki ürünleri code alanını anahtar olarak kullanarak by_code sözlüğünde indeksleyin.

#| exercise: hafta04-index
#| completion: false
#| persist: true
products = [
    {"code": "K1", "name": "Kalem", "stock": 18},
    {"code": "D2", "name": "Defter", "stock": 6},
    {"code": "S3", "name": "Silgi", "stock": 9},
]

by_code = {}

for product in products:
    # Burayı tamamlayın.
    pass

print(by_code["D2"])
#| exercise: hafta04-index
#| check: true
scope = {}
try:
    exec(user_code, scope)
    by_code = scope.get("by_code")
    correct = (
        isinstance(by_code, dict)
        and by_code.get("K1", {}).get("name") == "Kalem"
        and by_code.get("D2", {}).get("stock") == 6
        and by_code.get("S3", {}).get("name") == "Silgi"
    )
except Exception:
    correct = False

feedback = (
    {"correct": True, "message": "Kayıtlar code alanına göre doğru indekslendi."}
    if correct
    else {"correct": False, "message": "Her ürün için by_code[product['code']] = product kalıbını kullanın."}
)
feedback

Sözlüğün anahtarı product["code"], değeri ise product kaydının kendisi olabilir.

Solution.

for product in products:
    by_code[product["code"]] = product

12 Özetleme

Kayıtlardaki sayısal alanları özetlemek için biriktirici kalıbını kullanabiliriz:

#| edit: false
#| completion: false
students = [
    {"name": "Ayşe", "grade": 86},
    {"name": "Bora", "grade": 72},
    {"name": "Cem", "grade": 91},
]

total = 0
for student in students:
    total += student["grade"]

average = total / len(students) if students else 0
print("Ortalama:", average)

Bu bölümde önemli olan toplam hesabının kendisinden çok, kayıt yapısındaki belirli alanları sistematik biçimde işleyebilmenizdir.

13 İç içe yapıları okurken adım adım düşünün

Şu ifadeyi inceleyelim:

students[1]["courses"][0]

Bunu tek seferde değil adım adım okuyun:

  1. students[1] → ikinci öğrenci kaydı,
  2. ["courses"] → o kaydın ders listesi,
  3. [0] → ilk ders.

Bu alışkanlık, iç içe yapılardaki indeks/anahtar hatalarını azaltır.

14 Sığ kopya tekrar karşımıza çıkıyor

#| edit: false
#| completion: false
record = {"name": "Ayşe", "tags": ["python", "data"]}
copy_record = record.copy()
copy_record["tags"].append("web")

print(record)
print(copy_record)

.copy() dış sözlüğü kopyalar; içteki tags listesi paylaşılmaya devam eder. Bu, 2. haftadaki sığ kopya fikrinin kayıtlar üzerindeki karşılığıdır.

15 Küçük karar çalışması

Aynı öğrenci verisi için aşağıdaki gereksinimlerde hangi temsili önceliklendirirsiniz?

  1. Bütün öğrencileri kayıt sırasıyla ekranda göstermek.
  2. Öğrenci numarasına göre saniyede çok sayıda sorgu yapmak.
  3. Bölüm adına göre o bölümdeki öğrencilerin tamamına ulaşmak.

Seçenekler:

A. list[dict]
B. dict[number -> record]
C. dict[department -> list[record]]

Yanıtınızda yalnızca yapının adını değil, hangi işlemi kolaylaştırdığı bilgisini de yazın.

16 Bölüm özeti

  • Bir kayıt sözlükle temsil edilebilir.
  • Çok sayıda kayıt için list[dict] sıralı dolaşma, filtreleme ve raporlama açısından doğaldır.
  • Aynı kayıtları dict[id -> record] biçiminde indekslemek kimliğe göre sık erişimi kolaylaştırır.
  • dict[group -> list[record]] gruplama problemlerine uygundur.
  • Aynı kayıt nesneleri birden çok koleksiyon tarafından referans edilebilir.
  • Temsil seçimi sözdizimine değil işlem gereksinimine dayanır.
  • İç içe veri yapılarında sığ kopya davranışı önemini korur.

17 Kendinizi kontrol edin

  1. list[dict] hangi işlemlerde doğaldır?
  2. Öğrenci numarasına göre çok sık erişim gerekiyorsa dış temsil nasıl değiştirilebilir?
  3. Bölüme göre öğrenci gruplamak için hangi yapı kombinasyonu uygundur?
  4. students listesi ile by_number sözlüğü aynı kayıt nesnesini gösteriyorsa birindeki değişiklik neden diğerinde görünür?
  5. Filtreleme için neden çoğu zaman bütün kayıtları dolaşmak gerekir?
  6. Sığ kopyalanmış bir sözlüğün içindeki liste neden hâlâ paylaşılabilir?
Back to top