← Kursa Dön
📄 Text · 18 min

Dictionary (Sözlük)

Dictionary (sözlük), Python'un en güçlü ve en sık kullanılan veri yapılarından biri. Her eleman bir anahtar-değer (key-value) çiftinden oluşur. Anahtar benzersizdir ve ona karşılık gelen değere anında erişim sağlar.

Gerçek hayattaki bir telefon rehberi düşün: İsmi (anahtar) biliyorsan, numarasını (değer) anında bulabilirsin. A'dan Z'ye sırayla aramana gerek yok — direkt "Ahmet" diyorsun, numara geliyor. İşte dictionary tam olarak bu: O(1) zaman karmaşıklığıyla anahtar üzerinden değere erişim. Bu onu listelerdeki O(n) aramaya kıyasla inanılmaz hızlı yapar.


Dictionary Oluşturma

# Süslü parantezle (en yaygın)
ogrenci = {
    "isim": "Ahmet",
    "yas": 25,
    "sehir": "İstanbul",
}

# Boş dictionary
bos = {}
bos2 = dict()

# dict() constructor ile
ogrenci2 = dict(isim="Ayşe", yas=22, sehir="Ankara")
print(ogrenci2)  # {'isim': 'Ayşe', 'yas': 22, 'sehir': 'Ankara'}

# Tuple listesinden
ciftler = [("a", 1), ("b", 2), ("c", 3)]
d = dict(ciftler)
print(d)  # {'a': 1, 'b': 2, 'c': 3}

# zip ile
anahtarlar = ["isim", "yas", "sehir"]
degerler = ["Mehmet", 30, "İzmir"]
d = dict(zip(anahtarlar, degerler))
print(d)  # {'isim': 'Mehmet', 'yas': 30, 'sehir': 'İzmir'}

# fromkeys: Tüm key'lere aynı değer
harfler = dict.fromkeys("abc", 0)
print(harfler)  # {'a': 0, 'b': 0, 'c': 0}

Key Kuralları

Dictionary key'leri hashable olmalıdır. Yani immutable tipler key olabilir:

# ✅ Geçerli key'ler
d = {
    "string_key": 1,
    42: 2,
    3.14: 3,
    (1, 2): 4,     # Tuple hashable
    True: 5,
    None: 6,
    frozenset({1, 2}): 7,
}

# ❌ Geçersiz key'ler
# d = {[1, 2]: "değer"}      # TypeError: unhashable type: 'list'
# d = {{1, 2}: "değer"}      # TypeError: unhashable type: 'set'
# d = {{"a": 1}: "değer"}    # TypeError: unhashable type: 'dict'

⚠️ Dikkat: True ve 1, False ve 0 Python'da eşit kabul edilir ve aynı hash'e sahiptir. Bu yüzden aynı key olarak davranırlar: ``python d = {True: "bool", 1: "int"} print(d) # {True: 'int'} — 1, True'nun üzerine yazdı! ``


CRUD İşlemleri

Create & Read (Oluşturma & Okuma)

ogrenci = {"isim": "Ahmet", "yas": 25}

# Okuma — köşeli parantez
print(ogrenci["isim"])  # Ahmet
print(ogrenci["yas"])   # 25

# Olmayan key → KeyError!
# print(ogrenci["sinif"])  # KeyError: 'sinif'

Update (Güncelleme)

# Mevcut değeri güncelle
ogrenci["yas"] = 26
print(ogrenci)  # {'isim': 'Ahmet', 'yas': 26}

# Yeni key-value ekle (yoksa oluşturur)
ogrenci["sinif"] = "A"
print(ogrenci)  # {'isim': 'Ahmet', 'yas': 26, 'sinif': 'A'}

Delete (Silme)

ogrenci = {"isim": "Ahmet", "yas": 25, "sehir": "İstanbul"}

# del ile silme
del ogrenci["sehir"]
print(ogrenci)  # {'isim': 'Ahmet', 'yas': 25}

# pop ile silme (değeri döndürür)
yas = ogrenci.pop("yas")
print(yas)       # 25
print(ogrenci)   # {'isim': 'Ahmet'}

# pop — olmayan key, varsayılan değer
sonuc = ogrenci.pop("telefon", "Bulunamadı")
print(sonuc)  # Bulunamadı

# popitem — son eklenen key-value çiftini sil (Python 3.7+)
d = {"a": 1, "b": 2, "c": 3}
son = d.popitem()
print(son)  # ('c', 3)
print(d)    # {'a': 1, 'b': 2}

# clear — tümünü sil
d.clear()
print(d)  # {}

get() vs []: KeyError'dan Kaçınma

Bu çok önemli bir fark:

ogrenci = {"isim": "Ahmet", "yas": 25}

# [] — key yoksa HATA
# print(ogrenci["telefon"])  # KeyError: 'telefon'

# get() — key yoksa None döner (veya verdiğin varsayılan)
print(ogrenci.get("telefon"))          # None
print(ogrenci.get("telefon", "Yok"))   # Yok
print(ogrenci.get("isim"))             # Ahmet
print(ogrenci.get("isim", "Bilinmiyor"))  # Ahmet (key var, varsayılan kullanılmaz)

Ne Zaman Hangisi?

# [] kullan: Key'in var olduğundan EMİNSEN
config = {"host": "localhost", "port": 8080}
port = config["port"]  # Kesinlikle var

# get() kullan: Key olmayabilirse
kullanici = {"isim": "Ahmet"}
telefon = kullanici.get("telefon", "Belirtilmemiş")

# get() kullan: Döngü içinde güvenli erişim
for satir in veriler:
    deger = satir.get("opsiyonel_alan", "varsayilan")

setdefault(): Yoksa Ekle, Varsa Döndür

d = {"a": 1, "b": 2}

# Key varsa: mevcut değeri döndür (değiştirmez)
sonuc = d.setdefault("a", 999)
print(sonuc)  # 1
print(d)      # {'a': 1, 'b': 2} — değişmedi

# Key yoksa: ekle ve döndür
sonuc = d.setdefault("c", 3)
print(sonuc)  # 3
print(d)      # {'a': 1, 'b': 2, 'c': 3} — eklendi!

setdefault özellikle gruplama yaparken kullanışlı:

# Öğrencileri sınıflarına göre grupla
ogrenciler = [
    ("Ahmet", "A"), ("Ayşe", "B"), ("Mehmet", "A"),
    ("Fatma", "B"), ("Ali", "A"),
]

siniflar = {}
for isim, sinif in ogrenciler:
    siniflar.setdefault(sinif, []).append(isim)

print(siniflar)
# {'A': ['Ahmet', 'Mehmet', 'Ali'], 'B': ['Ayşe', 'Fatma']}

Dictionary Metodları

keys(), values(), items()

ogrenci = {"isim": "Ahmet", "yas": 25, "sehir": "İstanbul"}

# keys() — anahtarlar
print(ogrenci.keys())    # dict_keys(['isim', 'yas', 'sehir'])
print(list(ogrenci.keys()))  # ['isim', 'yas', 'sehir']

# values() — değerler
print(ogrenci.values())  # dict_values(['Ahmet', 25, 'İstanbul'])

# items() — anahtar-değer çiftleri (tuple olarak)
print(ogrenci.items())   # dict_items([('isim', 'Ahmet'), ...])

Bu view nesneleri canlı — dict değişince otomatik güncellenir:

d = {"a": 1, "b": 2}
k = d.keys()
print(k)  # dict_keys(['a', 'b'])

d["c"] = 3
print(k)  # dict_keys(['a', 'b', 'c']) — otomatik güncellendi!

update()

d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}

# Mevcut dict'i güncelle (çakışan key'lerde d2 kazanır)
d1.update(d2)
print(d1)  # {'a': 1, 'b': 20, 'c': 3}

# Keyword argümanlarıyla da çalışır
d1.update(d=4, e=5)
print(d1)  # {'a': 1, 'b': 20, 'c': 3, 'd': 4, 'e': 5}

Döngü ile Dictionary

kisi = {"isim": "Ahmet", "yas": 25, "sehir": "İstanbul"}

# Sadece key'ler (varsayılan)
for key in kisi:
    print(key)

# Key-value birlikte (en yaygın)
for key, value in kisi.items():
    print(f"{key}: {value}")

# Sadece değerler
for value in kisi.values():
    print(value)

Dict Comprehension

Liste comprehension gibi, dict'ler için de tek satırda oluşturma:

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

# Koşullu
cift_kareler = {x: x**2 for x in range(1, 11) if x % 2 == 0}
print(cift_kareler)  # {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# Mevcut dict'ten dönüştürme
fiyatlar = {"elma": 5, "armut": 7, "muz": 3}
indirimli = {urun: fiyat * 0.9 for urun, fiyat in fiyatlar.items()}
print(indirimli)  # {'elma': 4.5, 'armut': 6.3, 'muz': 2.7}

# 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)  # {'Ahmet': 85, 'Ayşe': 92, 'Mehmet': 78}

String İşlemlerinde Dict Comprehension

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

# Kelime uzunlukları
kelimeler = ["python", "java", "go", "rust", "javascript"]
uzunluklar = {k: len(k) for k in kelimeler}
print(uzunluklar)

Nested Dictionary

Gerçek dünya verileri genellikle iç içe yapıdadır:

okul = {
    "sinif_A": {
        "ogretmen": "Ali Hoca",
        "ogrenciler": [
            {"isim": "Ahmet", "not": 85},
            {"isim": "Ayşe", "not": 92},
        ]
    },
    "sinif_B": {
        "ogretmen": "Fatma Hoca",
        "ogrenciler": [
            {"isim": "Mehmet", "not": 78},
            {"isim": "Zeynep", "not": 88},
        ]
    }
}

# Derin erişim
print(okul["sinif_A"]["ogretmen"])  # Ali Hoca
print(okul["sinif_A"]["ogrenciler"][0]["isim"])  # Ahmet

# Derin güncelleme
okul["sinif_A"]["ogrenciler"][0]["not"] = 90

Güvenli Derin Erişim

# İç içe get — her seviyede güvenli
def derin_al(d, *keys, varsayilan=None):
    for key in keys:
        if isinstance(d, dict):
            d = d.get(key, varsayilan)
        else:
            return varsayilan
    return d

config = {"database": {"host": "localhost", "port": 5432}}

print(derin_al(config, "database", "host"))         # localhost
print(derin_al(config, "database", "password"))      # None
print(derin_al(config, "cache", "host"))             # None

Nested Dict Döngüsü

notlar = {
    "Ahmet": {"matematik": 85, "fizik": 72, "kimya": 90},
    "Ayşe": {"matematik": 92, "fizik": 88, "kimya": 95},
    "Mehmet": {"matematik": 78, "fizik": 65, "kimya": 80},
}

for ogrenci, dersler in notlar.items():
    ortalama = sum(dersler.values()) / len(dersler)
    print(f"{ogrenci}: {ortalama:.1f}")

collections Modülü: defaultdict ve Counter

defaultdict

Normal dict'te olmayan bir key'e erişmek KeyError verir. defaultdict ise otomatik varsayılan değer oluşturur:

from collections import defaultdict

# Varsayılan int (0)
sayac = defaultdict(int)
kelimeler = ["elma", "armut", "elma", "muz", "elma", "armut"]

for kelime in kelimeler:
    sayac[kelime] += 1  # Key yoksa otomatik 0 başlar

print(dict(sayac))  # {'elma': 3, 'armut': 2, 'muz': 1}

# Varsayılan list ([])
gruplar = defaultdict(list)
ogrenciler = [("A", "Ahmet"), ("B", "Ayşe"), ("A", "Mehmet"), ("B", "Fatma")]

for sinif, isim in ogrenciler:
    gruplar[sinif].append(isim)

print(dict(gruplar))  # {'A': ['Ahmet', 'Mehmet'], 'B': ['Ayşe', 'Fatma']}

# Varsayılan set
benzersiz = defaultdict(set)
veriler = [("A", 1), ("B", 2), ("A", 1), ("A", 3), ("B", 2)]

for kategori, deger in veriler:
    benzersiz[kategori].add(deger)

print(dict(benzersiz))  # {'A': {1, 3}, 'B': {2}}

defaultdict vs setdefault:

# setdefault ile
d = {}
d.setdefault("key", []).append(1)

# defaultdict ile — daha temiz
d = defaultdict(list)
d["key"].append(1)

Counter

Counter kelime/eleman sayımı için özelleştirilmiş bir dict:

from collections import Counter

# Listeden sayım
meyveler = ["elma", "armut", "elma", "muz", "elma", "armut"]
sayim = Counter(meyveler)
print(sayim)  # Counter({'elma': 3, 'armut': 2, 'muz': 1})

# String'den harf frekansı
harfler = Counter("mississippi")
print(harfler)  # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})

# En yaygın N eleman
print(harfler.most_common(2))  # [('i', 4), ('s', 4)]

# Aritmetik işlemler
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  # Counter({'a': 4, 'b': 3})
print(c1 - c2)  # Counter({'a': 2})

# Toplam
print(sayim.total())  # 6 (Python 3.10+)

💡 İpucu: Bir listede eleman frekansı hesaplamak istiyorsan, for döngüsü yerine Counter kullan. Hem daha kısa hem daha hızlı (C ile optimize edilmiş).


Dict Ordering ve Sıralama

Python 3.7+ Ekleme Sırası Garantisi

Python 3.7'den itibaren dictionary'ler ekleme sırasını korur. Bu bir uygulama detayı değil, dil spesifikasyonunun bir parçası:

d = {}
d["c"] = 3
d["a"] = 1
d["b"] = 2

for k, v in d.items():
    print(k, v)
# c 3
# a 1
# b 2 (ekleme sırasında)

Sıralama

d = {"banana": 3, "apple": 1, "cherry": 2}

# Key'e göre sıralı dict
sirali_key = dict(sorted(d.items()))
print(sirali_key)  # {'apple': 1, 'banana': 3, 'cherry': 2}

# Value'ya göre sıralı dict
sirali_value = dict(sorted(d.items(), key=lambda x: x[1]))
print(sirali_value)  # {'apple': 1, 'cherry': 2, 'banana': 3}

# Azalan sıra
sirali_azalan = dict(sorted(d.items(), key=lambda x: x[1], reverse=True))
print(sirali_azalan)  # {'banana': 3, 'cherry': 2, 'apple': 1}

Dict Merge (Birleştirme)

Unpacking ile (**) — Python 3.5+

d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}

# Birleştir — çakışmada sonraki kazanır
merged = {**d1, **d2}
print(merged)  # {'a': 1, 'b': 20, 'c': 3}

# Üç dict birleştirme
d3 = {"d": 4}
merged = {**d1, **d2, **d3}
print(merged)  # {'a': 1, 'b': 20, 'c': 3, 'd': 4}

| Operatörü — Python 3.9+

d1 = {"a": 1, "b": 2}
d2 = {"b": 20, "c": 3}

# | ile yeni dict (orijinaller değişmez)
merged = d1 | d2
print(merged)  # {'a': 1, 'b': 20, 'c': 3}

# |= ile yerinde güncelleme
d1 |= d2
print(d1)  # {'a': 1, 'b': 20, 'c': 3}

Karşılaştırma Tablosu

YöntemPythonYeni dict?Orijinal?
{**d1, **d2}3.5+✅ EvetDeğişmez
`d1 \d2`3.9+✅ EvetDeğişmez
d1.update(d2)Tümü❌ Hayırd1 değişir
`d1 \= d2`3.9+❌ Hayırd1 değişir

Pratik Örnekler

Örnek 1: Basit Cache (Memoization)

cache = {}

def fibonacci(n):
    if n in cache:
        return cache[n]

    if n <= 1:
        sonuc = n
    else:
        sonuc = fibonacci(n - 1) + fibonacci(n - 2)

    cache[n] = sonuc
    return sonuc

print(fibonacci(50))  # 12586269025 — cache sayesinde hızlı
print(f"Cache boyutu: {len(cache)}")  # 51

Örnek 2: Kelime Frekans Analizi

from collections import Counter

metin = """
Python programlama dili güzel bir dil
Python ile programlama çok kolay
programlama öğrenmek eğlenceli
"""

# Temizle ve say
kelimeler = metin.lower().split()
frekans = Counter(kelimeler)

print("En sık 5 kelime:")
for kelime, sayi in frekans.most_common(5):
    print(f"  '{kelime}': {sayi} kez")

Örnek 3: Veri Gruplama

from collections import defaultdict

siparisler = [
    {"musteri": "Ahmet", "urun": "Laptop", "tutar": 15000},
    {"musteri": "Ayşe", "urun": "Telefon", "tutar": 8000},
    {"musteri": "Ahmet", "urun": "Mouse", "tutar": 200},
    {"musteri": "Mehmet", "urun": "Klavye", "tutar": 500},
    {"musteri": "Ayşe", "urun": "Kulaklık", "tutar": 1000},
    {"musteri": "Ahmet", "urun": "Monitor", "tutar": 5000},
]

# Müşteriye göre grupla
musteri_siparisleri = defaultdict(list)
musteri_toplam = defaultdict(float)

for siparis in siparisler:
    musteri = siparis["musteri"]
    musteri_siparisleri[musteri].append(siparis["urun"])
    musteri_toplam[musteri] += siparis["tutar"]

for musteri in musteri_siparisleri:
    urunler = ", ".join(musteri_siparisleri[musteri])
    toplam = musteri_toplam[musteri]
    print(f"{musteri}: {urunler} (Toplam: {toplam:,.0f} TL)")

Örnek 4: İki Dict Karşılaştırma

def dict_farklari(d1, d2):
    """İki dictionary arasındaki farkları bul."""
    tum_keyler = set(d1) | set(d2)
    farklar = {}

    for key in tum_keyler:
        if key not in d1:
            farklar[key] = {"durum": "sadece d2'de", "deger": d2[key]}
        elif key not in d2:
            farklar[key] = {"durum": "sadece d1'de", "deger": d1[key]}
        elif d1[key] != d2[key]:
            farklar[key] = {"durum": "farklı", "d1": d1[key], "d2": d2[key]}

    return farklar

eski = {"isim": "Ahmet", "yas": 25, "sehir": "İstanbul"}
yeni = {"isim": "Ahmet", "yas": 26, "email": "ahmet@mail.com"}

for key, bilgi in dict_farklari(eski, yeni).items():
    print(f"  {key}: {bilgi}")

Performans Özellikleri

Dictionary Python'un en hızlı veri yapılarından biri — ama neden?

Hash Tablosu Nasıl Çalışır?

Dictionary, perde arkasında hash tablosu kullanır. Bir key eklediğinde Python:

  1. Key'in hash değerini hesaplar (hash(key))

  2. Bu hash'e göre bellekte bir konum belirler

  3. Değeri o konuma yazar

Arama yaparken aynı adımları izler — hash'e göre konuma gider, değeri okur. Bu yüzden O(1).

# Hash değerlerini görelim
print(hash("isim"))    # Sabit bir sayı
print(hash(42))        # 42 (küçük int'ler kendisine hash'lenir)
print(hash((1, 2)))    # Tuple hashable

# ❌ Unhashable — hash hesaplanamaz
# hash([1, 2])  # TypeError: unhashable type: 'list'

Gerçek Dünya Performansı

import time

# 1 milyon kayıt
n = 1_000_000
buyuk_dict = {f"key_{i}": i for i in range(n)}
buyuk_liste = [(f"key_{i}", i) for i in range(n)]

# Dict'te arama — O(1)
start = time.perf_counter()
_ = buyuk_dict["key_999999"]
dict_sure = time.perf_counter() - start

# Liste'de arama — O(n)
start = time.perf_counter()
for k, v in buyuk_liste:
    if k == "key_999999":
        break
liste_sure = time.perf_counter() - start

print(f"Dict arama:  {dict_sure:.8f}s")
print(f"Liste arama: {liste_sure:.4f}s")
print(f"Dict {liste_sure/dict_sure:.0f}x daha hızlı!")

Yaygın Hatalar

1. Döngüde Dictionary Değiştirme

d = {"a": 1, "b": 2, "c": 3}

# ❌ RuntimeError: dictionary changed size during iteration
# for key in d:
#     if d[key] < 2:
#         del d[key]

# ✅ Kopyası üzerinde dön
for key in list(d.keys()):
    if d[key] < 2:
        del d[key]
print(d)  # {'b': 2, 'c': 3}

# ✅ Veya comprehension ile yeni dict
d = {k: v for k, v in d.items() if v >= 2}

2. Mutable Default Key Tuzağı

# ❌ fromkeys ile mutable varsayılan — aynı listeyi paylaşır!
d = dict.fromkeys(["a", "b", "c"], [])
d["a"].append(1)
print(d)  # {'a': [1], 'b': [1], 'c': [1]} — Hepsi aynı liste!

# ✅ Comprehension ile
d = {k: [] for k in ["a", "b", "c"]}
d["a"].append(1)
print(d)  # {'a': [1], 'b': [], 'c': []}

3. Key Olarak Kullanılamayan Tipler

# Listeler hashable olmadığı için key olamaz
# d = {[1, 2]: "değer"}  # TypeError

# Çözüm: tuple'a çevir
d = {tuple([1, 2]): "değer"}
print(d[(1, 2)])  # değer

İleri Teknikler

Dictionary View Operasyonları

d1 = {"a": 1, "b": 2, "c": 3}
d2 = {"b": 2, "c": 4, "d": 5}

# Key'lerin kesişimi
ortak_keyler = d1.keys() & d2.keys()
print(ortak_keyler)  # {'b', 'c'}

# Key'lerin farkı
sadece_d1 = d1.keys() - d2.keys()
print(sadece_d1)  # {'a'}

# Item'ların kesişimi (key VE value eşleşmeli)
ortak_items = d1.items() & d2.items()
print(ortak_items)  # {('b', 2)}

dict Subclassing Alternatifleri

from collections import OrderedDict, ChainMap

# OrderedDict — Python 3.7+ sonrası çoğunlukla gereksiz
# Ama move_to_end() gibi ekstra metodları var
od = OrderedDict(a=1, b=2, c=3)
od.move_to_end("a")
print(list(od.keys()))  # ['b', 'c', 'a']

# ChainMap — birden fazla dict'i zincirleme ara
varsayilan = {"tema": "light", "dil": "tr", "font_size": 14}
kullanici = {"tema": "dark", "font_size": 16}

config = ChainMap(kullanici, varsayilan)
print(config["tema"])       # dark (kullanıcı ayarı)
print(config["dil"])        # tr (varsayılan)
print(config["font_size"])  # 16 (kullanıcı ayarı)

💡 İpucu: ChainMap aslında dict'leri birleştirmez — zincirleme arar. Bu, büyük dict'lerde kopyalama maliyetinden kurtarır ve değişikliklerin anında yansımasını sağlar.


Özet

  • Dictionary anahtar-değer çiftlerinden oluşur — key üzerinden O(1) erişim sağlar. Key'ler hashable (immutable) olmalıdır.

  • `get(key, default)` ile KeyError'dan kaçınırsın; setdefault() yoksa ekler varsa döndürür — gruplama işlemlerinde çok kullanışlı.

  • Dict comprehension ({k: v for ...}) tek satırda dict oluşturur — filtreleme, dönüştürme ve ters çevirme için ideal.

  • `defaultdict` ve `Counter` (collections modülü) yaygın dict pattern'larını basitleştirir — gruplama için defaultdict, sayım için Counter kullan.

  • Python 3.7+ ekleme sırasını garanti eder. Dict merge: {**d1, **d2} (3.5+) veya d1 | d2 (3.9+) ile birleştir.

  • Döngüde dict değiştirme RuntimeError verir — list(d.keys()) kopyası üzerinde dön veya comprehension ile yeni dict oluştur.