dict - En Çok Kullanılan Veri Yapısı!
Python'un yerleşik dictionary (dict) veri yapısı, hash table implementasyonudur!
O(1) demek, dict'te kaç eleman olursa olsun (1000 veya 1 milyon), bir anahtara erişim aynı sürede olur demektir.
Karşılaştırma:
• Liste: 1 milyon elemandan birini bulmak için belki 1 milyon adım gerekir (O(n))
• Dict: 1 milyon elemandan birini bulmak 1 adımda olur (O(1))
Düşünün: Telefon rehberinizde 1000 kişi var. Liste ile "Zeynep"i bulmak için A'dan Z'ye kadar tek tek bakarsınız. Dict ile direkt "Zeynep" deyince bulursunuz!
JSON (JavaScript Object Notation): İnternetteki veri değişiminin standart formatı. Bir web sitesinden hava durumu bilgisi aldığınızda, veri JSON olarak gelir ve Python'da otomatik olarak dict'e dönüşür.
Örnek:
# Hava durumu API'sinden gelen JSON
hava_durumu = {
"sehir": "İstanbul",
"sicaklik": 22,
"durum": "Güneşli"
}
print(hava_durumu["sicaklik"]) # 22
import math yazdığınızda Python ne yapar? Arka planda bir dict'e bakar!
Python, yüklenen tüm modülleri sys.modules adlı bir dict'te tutar. Böylece aynı modülü tekrar import ettiğinizde, diskten yeniden okumak yerine dict'ten hemen alır (çok hızlı!).
import sys
print("math" in sys.modules) # True (import edildiyse)
Bir class oluşturduğunuzda, o class'ın tüm özellikleri (attribute) aslında bir dict içinde saklanır!
object.__dict__ ile herhangi bir nesnenin iç yapısını görebilirsiniz:
class Ogrenci:
def __init__(self, ad, yas):
self.ad = ad
self.yas = yas
ali = Ogrenci("Ali", 20)
print(ali.__dict__) # {'ad': 'Ali', 'yas': 20}
Bu yüzden Python'da her şey dict üzerine kurulu diyebiliriz!
# dict oluşturma yöntemleri
print("=== dict Oluşturma ===")
# 1. Süslü parantez
ogrenci = {
"ad": "Ahmet",
"soyad": "Yılmaz",
"yas": 20,
"not": 85
}
print(f"1. Yöntem: {ogrenci}")
# 2. dict() fonksiyonu
ogrenci2 = dict(ad="Mehmet", soyad="Kaya", yas=22)
print(f"2. Yöntem: {ogrenci2}")
# 3. Listeden
veriler = [("ad", "Ayşe"), ("yas", 19)]
ogrenci3 = dict(veriler)
print(f"3. Yöntem: {ogrenci3}")
print("\n=== Erişim ===")
# Erişim - O(1)
print(f"Öğrencinin adı: {ogrenci['ad']}")
print(f"Öğrencinin notu: {ogrenci['not']}")
# Güvenli erişim (yoksa None döner)
print(f"Şube: {ogrenci.get('sube', 'Belirtilmemiş')}")
print("\n=== Ekleme/Güncelleme ===")
# Ekleme/Güncelleme - O(1)
ogrenci["sube"] = "A"
print(f"Şube eklendi: {ogrenci}")
ogrenci["not"] = 90 # Güncelleme
print(f"Not güncellendi: {ogrenci}")
print("\n=== Silme ===")
# Silme - O(1)
del ogrenci["sube"]
print(f"Şube silindi: {ogrenci}")
# pop ile silme (değer döndürür)
yas = ogrenci.pop("yas")
print(f"Yaş silindi (değer: {yas}): {ogrenci}")
print("\n=== Kontroller ===")
# Anahtar var mı? - O(1)
print(f"'ad' var mı? {'ad' in ogrenci}")
print(f"'yas' var mı? {'yas' in ogrenci}")
# Boyut
print(f"Eleman sayısı: {len(ogrenci)}")
notlar = {
"Ali": 85,
"Veli": 92,
"Ayşe": 78,
"Fatma": 95
}
print("=== Sadece Anahtarlar ===")
for isim in notlar:
print(isim)
print("\n=== Sadece Değerler ===")
for not_degeri in notlar.values():
print(not_degeri)
print("\n=== Anahtar-Değer Çiftleri ===")
for isim, not_degeri in notlar.items():
print(f"{isim}: {not_degeri}")
print("\n=== Şartlı Filtre (comprehension) ===")
# 90'dan yüksek notlar
yuksek_notlar = {isim: not_degeri
for isim, not_degeri in notlar.items()
if not_degeri >= 90}
print(f"90+ notlar: {yuksek_notlar}")
# Not ortalaması
ortalama = sum(notlar.values()) / len(notlar)
print(f"\nOrtalama: {ortalama:.1f}")
| İşlem | Ortalama | En Kötü |
|---|---|---|
d[key] (erişim) |
O(1) | O(n) |
d[key] = val (ekleme) |
O(1) | O(n) |
del d[key] |
O(1) | O(n) |
key in d |
O(1) | O(n) |
len(d) |
O(1) | O(1) |
*En kötü durum: Çok kötü hash fonksiyonu veya tüm çakışmalar
import time
# 10,000 elemanlı veri
n = 10000
liste = list(range(n))
sozluk = {i: i for i in range(n)}
print(f"=== {n} Elemanda Arama Hızı ===\n")
# Liste'de arama (O(n))
aranan = 9999
start = time.time()
for _ in range(1000):
result = aranan in liste
end = time.time()
list_time = (end - start) * 1000
print(f"Liste (O(n)): {list_time:.2f} ms")
# dict'te arama (O(1))
start = time.time()
for _ in range(1000):
result = aranan in sozluk
end = time.time()
dict_time = (end - start) * 1000
print(f"dict (O(1)): {dict_time:.2f} ms")
speedup = list_time / dict_time
print(f"\n⚡ dict {speedup:.0f}x daha hızlı!")
# Bellek kullanımı
import sys
print(f"\n=== Bellek Kullanımı ===")
print(f"Liste: {sys.getsizeof(liste):,} byte")
print(f"dict: {sys.getsizeof(sozluk):,} byte")
print(f"dict ~{sys.getsizeof(sozluk) / sys.getsizeof(liste):.1f}x daha fazla bellek")
# ❌ YANLIŞ
d = {[1, 2]: "değer"} # TypeError!
# ✅ DOĞRU (tuple kullan)
d = {(1, 2): "değer"}
# ❌ YANLIŞ
for key in d:
del d[key] # RuntimeError!
# ✅ DOĞRU
for key in list(d.keys()):
del d[key]
d.get(key, default)d.setdefault(key, [])d1.update(d2) veya {**d1, **d2}{v: k for k, v in d.items()}
Normalde olmayan bir anahtara erişirseniz KeyError hatası alırsınız. get() ile bu hatadan kaçınırsınız:
ogrenci = {"ad": "Ali", "yas": 20}
# ❌ Hatalı - KeyError verir!
# print(ogrenci["not"])
# ✅ Güvenli - Yoksa 0 döner
print(ogrenci.get("not", 0)) # 0
# ✅ Yoksa "Bilinmiyor" yaz
print(ogrenci.get("sube", "Bilinmiyor")) # Bilinmiyor
💡 İpucu: API'den gelen verilerde bazı alanlar eksik olabilir. get() ile programınız çökmez!
Gruplama yaparken çok kullanışlı! Anahtar yoksa oluşturur, varsa dokunmaz:
# Öğrencileri sınıflarına göre grupla
ogrenciler = [("A", "Ali"), ("B", "Veli"), ("A", "Ayşe"), ("B", "Fatma")]
siniflar = {}
for sinif, isim in ogrenciler:
# sinif yoksa boş liste oluştur, sonra ekle
siniflar.setdefault(sinif, []).append(isim)
print(siniflar)
# {'A': ['Ali', 'Ayşe'], 'B': ['Veli', 'Fatma']}
💡 Alternatif: from collections import defaultdict kullanabilirsiniz.
İki dict'i birleştirmenin birkaç yolu var:
varsayilan = {"renk": "mavi", "boyut": "M", "fiyat": 100}
kullanici = {"renk": "kırmızı", "indirim": True}
# Yöntem 1: update() - d1'i değiştirir
d1 = varsayilan.copy()
d1.update(kullanici)
print(d1) # {'renk': 'kırmızı', 'boyut': 'M', 'fiyat': 100, 'indirim': True}
# Yöntem 2: ** unpacking - yeni dict oluşturur
d2 = {**varsayilan, **kullanici}
print(d2) # Aynı sonuç
# Python 3.9+ için:
d3 = varsayilan | kullanici # Pipe operatörü!
💡 İkinci dict'teki değerler birincinin üzerine yazar (renk: kırmızı oldu)
Anahtar-değer çiftlerini yer değiştirmek için dict comprehension:
# Ülke kodları
kodlar = {"TR": "Türkiye", "US": "Amerika", "DE": "Almanya"}
# Tersine çevir: Ülke adından koda
ulke_kod = {v: k for k, v in kodlar.items()}
print(ulke_kod)
# {'Türkiye': 'TR', 'Amerika': 'US', 'Almanya': 'DE'}
# Artık ülke adıyla kodu bulabiliriz
print(ulke_kod["Türkiye"]) # TR
⚠️ Dikkat: Eğer değerler benzersiz değilse (tekrar ediyorsa), veri kaybı olur!
Hashable = Hash'lenebilir = Hash fonksiyonu uygulanabilir demektir.
Bir nesnenin hashable olması için iki şart gerekir:
int | 42, 0, -17 |
float | 3.14, 2.718 |
str | "merhaba", "Ali" |
bool | True, False |
tuple | (1, 2, 3) |
frozenset | frozenset({1, 2}) |
None | None |
Ortak özellik: Hepsi değişmez (immutable)!
list | [1, 2, 3] |
dict | {"a": 1} |
set | {1, 2, 3} |
Ortak özellik: Hepsi değişebilir (mutable)!
Düşünün: Bir dict'e anahtar olarak liste eklediniz. Sonra o listeyi değiştirdiniz...
# Hayal edin ki bu çalışsaydı (aslında hata verir)
anahtar = [1, 2, 3]
d = {anahtar: "değer"} # hash([1,2,3]) = 123 diyelim
# Sonra listeyi değiştirdik
anahtar.append(4) # Artık [1, 2, 3, 4]
# Problem: Hash değeri değişti! (hash = 456 oldu)
# d[anahtar] artık bulunamaz çünkü yanlış yere bakıyor!
Sonuç: Python, veri tutarsızlığını önlemek için mutable tipleri anahtar olarak yasaklar!
# hash() fonksiyonu ile test et
print(hash(42)) # ✅ Çalışır: -123456...
print(hash("merhaba")) # ✅ Çalışır: 789012...
print(hash((1, 2, 3))) # ✅ Çalışır: -345678...
# Hashable değilse TypeError verir
try:
print(hash([1, 2, 3]))
except TypeError as e:
print(f"❌ Hata: {e}")
# "unhashable type: 'list'"
💡 İpucu: hash(nesne) hata vermezse, o nesne dict anahtarı olabilir!
Liste anahtar olarak kullanılamaz ama tuple kullanılabilir:
# Koordinatları anahtar olarak kullanalım
koordinatlar = {}
# ❌ Liste ile - HATA!
# koordinatlar[[10, 20]] = "Ev" # TypeError!
# ✅ Tuple ile - ÇALIŞIR!
koordinatlar[(10, 20)] = "Ev"
koordinatlar[(30, 40)] = "Okul"
koordinatlar[(50, 60)] = "Market"
print(koordinatlar[(10, 20)]) # "Ev"
# Pratik kullanım: Satranç tahtası
tahta = {}
tahta[("a", 1)] = "Kale"
tahta[("e", 1)] = "Kral"