Ana içeriğe geç
Kesit Python
Temel · Veri yapıları

Sözlükler (dict)

Müşteri ve sipariş kayıtlarını anahtar-değer çiftleriyle tutar; get, items ve update ile güvenle okur-günceller; iç içe sözlükle JSON benzeri yapı kurar ve sayım kalıbını öğrenirsin.

Uygulamada aç, 21 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

İsimle erişmek

Listeler dersinde bir siparişi ["AM-101", "İzmir", 1499.9] diye tutmuştun. Üç ay sonra siparis[2] gördüğünde bunun tutar olduğunu hatırlayacak mısın? Sözlük (dictionary, dict) her değere bir anahtar (key) verir: siparis["tutar"]. Anadolu Market'in müşteri kaydı, Ilgaz Bank'ın hesap bilgisi, bir API'den dönen yanıt: iş dünyasındaki verinin büyük kısmı bu biçimdedir.

musteri = {
    "no": "M-101",
    "ad": "Ayşe Yılmaz",
    "sehir": "İzmir",
    "bakiye": 12500.0,
    "aktif": True,
}
print(musteri["ad"])
print(musteri["bakiye"] + 500)
print(len(musteri), "alan")
print(type(musteri))

Süslü parantez içinde anahtar: değer çiftleri. Anahtar genelde metindir ama sayı ve demet de olabilir; değişmez olması yeter. Değer ise her şey olabilir: sayı, metin, liste, hatta başka bir sözlük. Python 3.7'den beri sözlük, anahtarların ekleme sırasını korur.

Okuma: köşeli parantez mi get mi?

Var olmayan anahtarı köşeli parantezle istersen KeyError alırsın. get ise bulamazsa None ya da senin verdiğin varsayılanı döndürür ve programı durdurmaz.

musteri = {"no": "M-101", "ad": "Ayşe Yılmaz", "sehir": "İzmir"}
print(musteri.get("sehir"))
print(musteri.get("telefon"))              # None
print(musteri.get("telefon", "kayıtlı değil"))
print("telefon" in musteri)                # anahtar var mı?
print("ad" in musteri)
try:
    print(musteri["telefon"])
except KeyError as hata:
    print("KeyError:", hata)

Kural: anahtarın kesin var olduğunu biliyorsan köşeli parantez (hata alırsan bu gerçek bir sorundur ve görmek istersin). Eksik olabilecek alanlarda get. in operatörü sözlükte anahtarları arar, değerleri değil.

Ekleme, güncelleme, silme

hesap = {"no": "TR-5501", "bakiye": 10000}
hesap["sahip"] = "Mehmet Kaya"        # yeni anahtar ekle
hesap["bakiye"] = hesap["bakiye"] - 750   # değeri değiştir
hesap["bakiye"] += 2000               # kısayol
print(hesap)
hesap.update({"sube": "Konak", "bakiye": 11250})   # toplu güncelle
print(hesap)
silinen = hesap.pop("sube")           # anahtarı çıkar, değeri döndür
print(silinen, hesap)
del hesap["sahip"]
print(hesap)
print(hesap.pop("yok", "bulunamadı")) # varsayılanlı pop

Aynı anahtara ikinci kez atama yapmak üzerine yazar; sözlükte anahtar tekrarı yoktur. update başka bir sözlükten alanları alır: var olanları günceller, olmayanları ekler. pop ve del siler; pop değeri geri verdiği için daha sık kullanılır.

İşlemYazımEksik anahtarda
Okud["k"]KeyError
Güvenli okud.get("k", varsayilan)varsayılan döner
Ekle / güncelled["k"] = veklenir
Toplu güncelled.update(baska)eklenir
Sil ve ald.pop("k")KeyError (varsayılan verilmezse)
Sildel d["k"]KeyError
Var mı?"k" in dFalse

keys, values, items

Sözlüğün üç görünümü vardır: anahtarlar, değerler ve çiftler. Bunlar liste gibi gezilebilir; döngüler dersinde her gün kullanacaksın. Şimdilik listeye çevirip bakalım:

fiyatlar = {"kahve makinesi": 1499.9, "kalem": 2.5, "defter": 45}
print(list(fiyatlar.keys()))
print(list(fiyatlar.values()))
print(list(fiyatlar.items()))
print("Toplam:", sum(fiyatlar.values()))
print("En pahalı:", max(fiyatlar, key=fiyatlar.get))
urun, fiyat = list(fiyatlar.items())[0]     # demet açma!
print(urun, fiyat)

items her çifti bir demet olarak verir; demet açmayı öğrendiğin için urun, fiyat = ... yazabildin. max(fiyatlar, key=fiyatlar.get) kalıbı "değeri en büyük olan anahtarı bul" demektir ve raporlarda çok geçer.

İç içe sözlük ve JSON'a hazırlık

Bir müşterinin birden fazla siparişi, bir siparişin birden fazla ürünü var. Değer olarak liste ve sözlük kullanarak hiyerarşi kurarsın:

musteri = {
    "no": "M-101",
    "ad": "Ayşe Yılmaz",
    "adres": {"sehir": "İzmir", "ilce": "Konak", "posta": "35220"},
    "siparisler": [
        {"kod": "AM-101", "tutar": 1499.9, "teslim": True},
        {"kod": "AM-103", "tutar": 89.5, "teslim": False},
    ],
}
print(musteri["adres"]["sehir"])
print(musteri["siparisler"][0]["kod"])
print(musteri["siparisler"][-1]["teslim"])
musteri["adres"]["ilce"] = "Bornova"
musteri["siparisler"].append({"kod": "AM-110", "tutar": 250, "teslim": False})
print(len(musteri["siparisler"]), "sipariş")
import json
print(json.dumps(musteri, ensure_ascii=False, indent=2)[:80])

Zincirleme köşeli parantez derinlere iner: önce adres sözlüğü, sonra içindeki şehir. Son satırlardaki json modülü bu yapıyı metne çevirir. JSON, web servislerinin ve yapılandırma dosyalarının ortak dilidir ve Python sözlüğüyle neredeyse birebir aynı görünür: süslü parantez, anahtar-değer, listeler. Sözlüğü iyi kavradıysan JSON'ı zaten biliyorsun; JSON ve CSV dersinde dosyadan okuyup yazacağız.

Kopyalama tuzağı burada da varyedek = musteri kopya değildir; musteri.copy() ise yalnızca dış sözlüğü kopyalar, içindeki adres sözlüğü ve sipariş listesi paylaşılır. İç içe yapıyı tamamen kopyalamak için copy.deepcopy kullan.

Sayım kalıbı

Analistin en sık yazdığı kod parçalarından biri: bir listedeki her değerin kaç kez geçtiğini bulmak. Şehir bazlı sipariş adedi, ürün bazlı satış, hata türü bazlı log sayısı. Sözlük bunun için biçilmiş kaftan; anahtar değer, değer ise sayaç olur. Döngüyü henüz görmediğin için önce adım adım, sonra döngülü hâlini gösteriyorum:

sehirler = ["İzmir", "Ankara", "İzmir", "Bursa", "İzmir", "Ankara"]
sayim = {}
sayim["İzmir"] = sayim.get("İzmir", 0) + 1     # ilk kez: 0 + 1
sayim["Ankara"] = sayim.get("Ankara", 0) + 1
sayim["İzmir"] = sayim.get("İzmir", 0) + 1     # ikinci kez: 1 + 1
print(sayim)

sayim = {}
for sehir in sehirler:          # döngü: her şehir için aynı satır
    sayim[sehir] = sayim.get(sehir, 0) + 1
print(sayim)
print("En çok:", max(sayim, key=sayim.get))

Kalıbın kalbi tek satır: sayim[x] = sayim.get(x, 0) + 1. get'in varsayılanı 0 olduğu için ilk karşılaşmada hata çıkmaz. Döngüler dersinde for satırını tam olarak anlayacaksın; şimdilik "listenin her elemanı için bu satırı çalıştır" diye oku. Aynı işi hazır yapan collections.Counter da var; Orta seviyede göreceksin.

Gruplama kalıbıSaymak yerine toplamak istersen aynı yapı çalışır: toplam[sehir] = toplam.get(sehir, 0) + tutar. Şehir bazlı ciro bu tek satırdır.
Anahtar olarak ne kullanmalı?Metin, sayı ve demet anahtar olabilir; liste olamaz (değişebilir). "Şehir ve yıl" gibi bileşik anahtar için demet kullan: ciro[("İzmir", 2025)].
Derinleş

Sözlükleri birleştirmek

İki kaynaktan gelen ayarları ya da fiyat listelerini birleştirmek günlük iştir. Python 3.9'dan beri bunun için | (yeni sözlük) ve |= (yerinde) operatörleri var. Aynı anahtar iki tarafta da varsa sağdaki kazanır. Eski kodlarda aynı işi {**a, **b} ya da a.update(b) ile görürsün.

varsayilan = {"para_birimi": "TL", "kdv": 0.20, "kargo": 49.9}
kampanya = {"kargo": 0, "indirim": 0.10}

ayar = varsayilan | kampanya          # yeni sözlük, sağdaki kazanır
print(ayar)
print(varsayilan)                     # değişmedi

eski_yol = {**varsayilan, **kampanya} # aynı sonuç
print(ayar == eski_yol)

varsayilan |= {"kdv": 0.10}           # yerinde güncelleme
print(varsayilan["kdv"])

Birleştirme üzerine yazar, toplamaz. İki deponun stoklarını toplamak istiyorsan döngüyle get(..., 0) kalıbını kullanırsın; aşağıdaki alıştırmalarda bunu yapacaksın.

Silmenin dört yolu

YöntemDönen değerAnahtar yoksaNe zaman?
del d[k]-KeyErrorAnahtarın var olduğundan eminsen
d.pop(k)Silinen değerKeyErrorDeğeri kullanacaksan
d.pop(k, varsayilan)Değer ya da varsayılanVarsayılanı döndürür"Varsa al ve sil"
d.popitem()Son eklenen (anahtar, değer)Boşsa KeyErrorYığın gibi tüketmek
d.clear()None-Hepsini boşalt
kuponlar = {"YAZ10": 10, "ILK50": 50}
indirim = kuponlar.pop("YAZ10", 0)    # kullanılan kupon silinir
print(indirim, kuponlar)
print(kuponlar.pop("SAHTE", 0))       # yoksa 0, hata yok
sepet = {"çay": 2, "kahve": 1}
print(sepet.popitem())                # en son eklenen: ('kahve', 1)

Gezerken değiştirme tuzağı

Bir sözlüğü for ile gezerken ona anahtar ekler ya da silersen Python RuntimeError: dictionary changed size during iteration verir. Çözüm: anahtarların bir kopyası üzerinde gez (list(d)) ya da silinecekleri önce topla, sonra sil. Var olan anahtarın değerini değiştirmek ise güvenlidir.

stok = {"çay": 0, "kahve": 5, "şeker": 0, "su": 12}
for urun in list(stok):               # kopya üzerinde gez
    if stok[urun] == 0:
        del stok[urun]
print(stok)

for urun in stok:                     # değer güncellemek serbest
    stok[urun] += 10
print(stok)

Görünümler (views) canlıdır ve küme gibi davranır

keys(), values(), items() kopya değil, sözlüğe açılan canlı pencerelerdir (view): sözlük değişince görünüm de değişir. Ayrıca keys() küme işlemlerini destekler; iki fiyat listesini karşılaştırırken kümeye çevirmene bile gerek kalmaz.

eski = {"çay": 120, "kahve": 340, "kek": 90}
yeni = {"çay": 125, "kahve": 340, "su": 15}
anahtarlar = yeni.keys()
print("Eklenen:", anahtarlar - eski.keys())
print("Kalkan:", eski.keys() - anahtarlar)
ortak = sorted(eski.keys() & anahtarlar)
print("Fiyatı değişen:", [u for u in ortak if eski[u] != yeni[u]])
yeni["süt"] = 40
print(len(anahtarlar))                 # görünüm güncellendi: 4

Son satırdaki köşeli parantezli tek satırlık döngü bir liste comprehension'dır; Comprehension dersinde ayrıntılı göreceksin. Sözlük comprehension'ı da ({u: f * 1.2 for u, f in fiyat.items()}) orada.

Değerde arama yavaştır

"İzmir" in musteri yalnızca anahtarlara bakar ve karma (hash) sayesinde neredeyse anında yanıt verir. "İzmir" in musteri.values() ise listedeki gibi baştan sona tarar. Değere göre sık arama yapıyorsan, ters sözlüğü bir kez kurup sonra anahtarla aramak çok daha hızlıdır. Örneğin müşteri numarası → e-posta sözlüğün varsa ve sürekli "bu e-posta kimin?" diye soruyorsan, e-posta → müşteri numarası sözlüğünü bir kez üret.

eposta = {"M-1": "ayse@ornek.com", "M-2": "can@ornek.com"}
print("can@ornek.com" in eposta)            # False: anahtarlara bakar
print("can@ornek.com" in eposta.values())   # True ama yavaş tarama
kimin = {}
for no, adres in eposta.items():
    kimin[adres] = no
print(kimin["can@ornek.com"])                 # hızlı ters arama

İç içe sözlüklerde güvenli erişimi (.get zinciri) ve derin kopyayı İç içe veri yapıları dersinde ayrıntılı göreceksin; buradaki kalıplar o dersin temelidir.

Değere göre sıralamak ve ilk N

Sözlük ekleme sırasını korur ama kendisi sıralanmaz. "En çok satan 3 ürün" için öğeleri (items()) değere göre sıralarsın. İki kısa yol var: anahtarları key=d.get ile sıralamak ya da çiftleri bir anahtar fonksiyonla sıralamak. Eşitlik kuralı gerekiyorsa demet anahtar kullan.

satis = {"çay": 120, "kahve": 340, "su": 120, "kek": 210}
print(sorted(satis, key=satis.get, reverse=True)[:3])

def adet_sonra_ad(cift):
    urun, adet = cift
    return (-adet, urun)             # adet azalan, eşitlikte ad artan

for urun, adet in sorted(satis.items(), key=adet_sonra_ad):
    print(f"{urun:<6}{adet:>5}")

Ters çevirme ve gruplama

Sözlüğü ters çevirmek (değer → anahtar) değerler benzersizse tek döngüdür. Değerler tekrar ediyorsa bilgi kaybolmasın diye her değere bir liste bağlarsın; bu da gruplama kalıbıdır. setdefault(k, []) anahtar yoksa boş listeyi koyup döndürür, varsa var olanı döndürür.

bolge = {"İzmir": "Ege", "Manisa": "Ege", "Ankara": "İç Anadolu", "Konya": "İç Anadolu", "Bursa": "Marmara"}
gruplar = {}
for sehir, b in bolge.items():
    gruplar.setdefault(b, []).append(sehir)
print(gruplar)
dict.fromkeys ile liste paylaşımıdict.fromkeys(["İzmir", "Ankara"], []) her anahtara aynı listeyi bağlar: birine eklediğin herkese görünür. Değişebilir varsayılan gerekiyorsa setdefault ya da Collections dersinde göreceğin defaultdict(list) kullan. Sayım için hazır araç collections.Counter da o derste.
Eşitlik sıraya bakmaz{"a": 1, "b": 2} == {"b": 2, "a": 1} True'dur. Testlerde sözlükleri doğrudan == ile karşılaştırabilirsin; sıralama ancak yazdırırken önemlidir.
Gerçek işteUygulamalar ayarlarını çoğunlukla katmanlı tutar: kod içindeki varsayılanlar, üstüne yapılandırma dosyası, en üstte ortam değişkenleri. Bu katmanlar tam olarak varsayilan | dosya | ortam birleştirmesidir; sağdaki kazanır. Fiyat ya da stok listesi senkronizasyonunda da ilk iş iki sözlüğün anahtar farkını (eklenen/kalkan) ve ortak anahtarlarda değeri değişenleri çıkarmaktır.
Uzmanlaş

Sözlük neden hızlı?

Sözlük, kümeyle aynı karma tablo (hash table) yapısını kullanır. Anahtarın karması hesaplanır ve değer doğrudan o adreste aranır; sözlük bir milyon kayıt da tutsa okuma süresi değişmez. Listede "no'su M-101 olan müşteri" aramak baştan sona taramadır. Bu yüzden kayıtları numarayla erişilecekse liste değil sözlükte tut: musteriler["M-101"].

setdefault ve defaultdict

Sayım kalıbındaki get(x, 0) yerine sayim.setdefault(x, 0) da yazılır; anahtar yoksa ekler ve değeri döndürür. Gruplama için collections.defaultdict(list) daha temizdir: gruplar[sehir].append(siparis) tek satırda, var mı diye bakmadan. Standart kütüphane dersinde bu araçları ayrıntılı ele alacağız.

Veri modeli olarak sözlük: sınırlar

Sözlük esnektir ama "ad" yerine yanlışlıkla "adi" yazarsan Python şikâyet etmez; sessizce yeni alan ekler. Büyük projelerde bu tür yazım hatalarını yakalamak için kayıtları dataclass ya da TypedDict ile tanımlarsın. Sözlük veri taşımak için, dataclass veri tanımlamak için. Orta seviyede bu ayrımı yaşayarak öğreneceksin.

Alıştırmalara geç (21)