Anahtarla erişim ve benzersiz değerler
zip() ile birlikte dolaşmıştık.Bu kod çalışır. Ancak adla notun eşleşmesi, iki listenin sırasının korunmasına bağlıdır.
Bu gibi problemleri yönetebilmek için anahtar değer eşleşmesini sağlayabilen bir veri yapısına ihtiyacımız var.
all() ve any() sonuçlarını yorumlayacağız.Sözlük (dict), anahtar-değer çiftleri tutan bir koleksiyondur. Listede bir elemana konumuyla, sözlükte ise anahtarıyla erişiriz. Sözlüğe yeni bir çift ekleyebilir, mevcut değeri değiştirebilir veya bir çifti silebiliriz.
Süslü parantez içindeki her kayıt anahtar: değer biçimindedir. Aşağıda öğrenci adları anahtar, notları ise değerdir. Bir notu okumak için öğrencinin adını köşeli parantez içinde yazarız.
"Ada" anahtarı bizi 85 değerine götürür. Farklı anahtarların değeri aynı olabilir.
Henüz hiç not girilmediyse {} ile boş bir sözlük oluşturabiliriz. len() sözlükteki anahtar-değer çiftlerinin sayısını verir.
Boş sözlükte okunacak bir anahtar yoktur. Birazdan, bir anahtarın bulunmadığı durumda ne olduğunu göreceğiz.
Şimdi Mert’in notunu aradığımızı düşünelim. Sözlükte Mert olmadığı için notlar["Mert"] bir not döndürmez, KeyError hatası verir.
Aradığımız adın kayıtlı olup olmadığını her zaman bilemeyiz. Eksik kayıt olasılığını da hesaba katmalıyız.
[] ve get()Bir anahtarın bulunmayabileceğini biliyorsak get() kullanabiliriz. Anahtar yoksa hata yerine None döner. İkinci argümanla "Kayıt yok" gibi başka bir sonuç da belirleyebiliriz.
in, sözlükte anahtar arar. Mert kayıtlı olmadığı için sonuç False olur. get() ise aynı durumda belirlediğimiz varsayılan değeri döndürür.
get() sonucunu doğrudan koşulda kullanırsak sıfır notunu gözden kaçırabiliriz. 0 geçerli bir nottur ama koşulda False sayılır. Kaydın varlığını kontrol etmek için in kullanırız.
sozluk[anahtar] = deger yazdığımızda anahtar yoksa yeni bir çift eklenir. Anahtar zaten varsa yalnızca değeri değişir; çiftlerin sayısı artmaz.
Python sözlükleri ekleme sırasını korur. Bir anahtarın değerini güncellemek onun sırasını değiştirmez. Bu sıra notları puana göre dizmez; bunun için ayrıca sıralama yapmalıyız.
update()Birden fazla not aynı anda geldiyse her anahtara ayrı ayrı atama yapmak yerine update() kullanabiliriz. Var olan anahtarların değerleri değişir; yeni anahtarlar sözlüğe eklenir.
update() sözlüğü yerinde değiştirir ve None döndürür. sonuc = notlar.update(...) yazarsak sonuc bir sözlük değil, None olur.
Aynı anahtarı iki kez yazarsak sözlükte iki kayıt oluşmaz. Son yazdığımız değer öncekinin yerini alır. Aynı adı taşıyan öğrencileri ayırmak için anahtar olarak öğrenci numarası kullanmalıyız.
dict(zip(adlar, notlar)) kısa bir yazımdır ama aynı ad tekrar ederse önceki not silinir. Ayrıca zip() kısa liste bitince durur; listelerin uzunlukları farklıysa bazı kayıtlar hiç oluşmaz. Bundan sonraki öğrenci kayıtlarında numarayı anahtar olarak kullanacağız.
Python’da hash, ayrı bir veri tipi değil, hash() fonksiyonunun hesapladığı tam sayıdır. Sözlük, bir anahtarı ararken bu sayıdan yararlanır.
Bir nesnenin hash değeri yaşamı boyunca değişmiyorsa ve eşit nesneler aynı değeri veriyorsa o nesneye hashlenebilir deriz.
Stringler ve sayılar sözlük anahtarı olabilir. Bir demeti de anahtar olarak kullanabiliriz, ancak içindeki bütün değerlerin hashlenebilir olması gerekir. Liste anahtar olamaz; sözlükte değer olarak tutulabilir.
Örneğin (1, [2]) bir demettir ama içindeki liste nedeniyle anahtar olarak kullanılamaz. Demetin kendisinin değiştirilememesi yeterli değildir.
pop() ve delpop(anahtar) çifti sözlükten çıkarır ve değerini döndürür. Anahtar yoksa hata almamak için ikinci argümanda varsayılan bir değer verebiliriz. del de çifti siler, ancak değer döndürmez.
Sözlük üzerinde doğrudan dolaştığımızda anahtarları alırız. Bunu açıkça keys() ile de yazabiliriz. Anahtar ve değeri birlikte kullanacaksak items() seçeriz. items() her adımda bir (anahtar, değer) çifti verir.
keys()keys() anahtarların bir görünümünü verir; yeni bir liste oluşturmaz. Sözlüğe sonradan anahtar eklersek görünüm de bu değişikliği yansıtır. Aşağıdaki örnek için doğrudan for numara in notlar yazmak, anahtarlar üzerinde dolaşmak için hâlâ yeterlidir.
values() sözlükteki değerlerin bir görünümünü verir. Bu örnekte değerler notlardır, öğrenci numaraları değil. 85 in notlar.values() ifadesi notlar arasında 85 arar. Aynı değerleri toplayarak ortalama da hesaplayabiliriz. Sözlük boşsa sıfıra bölmemek için önce kayıt olup olmadığını kontrol ederiz.
Bir kırtasiyenin satış listesinde her satış için bir ürün adı bulunuyor. Her üründen kaç tane satıldığını sayarken get(urun, 0) kullanabiliriz. Ürünle ilk kez karşılaşırsak sayı sıfırdan başlar. Daha önce gördüysek sözlükteki sayıyı alıp artırırız.
Burada get() ile ürünün kayıtlı olup olmadığını sormuyoruz. Ürün sözlükte yoksa saymaya sıfırdan başlıyoruz.
setdefault()Sayma örneğinde get(urun, 0) eksik ürün için sıfır verdi; yeni kaydı sayac[urun] = ... ataması oluşturdu. Bazen başlangıç değerini ekleme işini tek adımda yapmak isteriz. Öğrenci başvurularında durumu henüz girilmemiş bir numaraya “Bekliyor” kaydı açalım. setdefault() anahtar yoksa bu değeri ekler; kayıt zaten varsa mevcut değere dokunmaz.
setdefault() her iki durumda da anahtarın sözlükteki değerini döndürür. Sadece okumak istiyorsak get() kullanırız.
dict.fromkeys()Elimizde öğrenci numaraları var, ancak notlar henüz girilmemiş. dict.fromkeys() numaraları anahtar yaparak yeni bir sözlük oluşturur. İkinci argümanı vermezsek her anahtarın değeri None olur; bunu sıfır notundan farklı olarak “henüz not girilmedi” anlamında kullanabiliriz.
dict.fromkeys(numaralar, []) yazarsak bütün anahtarlar aynı listeyi paylaşır. Her öğrenci için ayrı liste gerektiğinde sözlük comprehension’ı kullanabiliriz; bu farkı birazdan bir örnekte göreceğiz.
Önceki derste list comprehension ile yeni bir liste oluşturmuştuk. Sözlük comprehension’da her kayıt anahtar: değer biçiminde yazılır. Bu örnekte notları değiştirmeden, geçen öğrencilerden yeni bir sözlük oluşturuyoruz.
Bir öğrencinin birden fazla notunu tutacaksak her anahtarın değeri ayrı bir liste olmalıdır. dict.fromkeys(numaralar, []) bütün anahtarları aynı listeye bağlar. Comprehension ise her numara için yeni bir liste oluşturur.
reversed()reversed(notlar) anahtarları eklenme sırasının tersine verir. Notları büyükten küçüğe sıralamaz ve sözlüğün kendisini değiştirmez.
reversed() sözlüğe özgü bir metot değil, listelerde ve demetlerde de çalışan yerleşik bir fonksiyondur. Sırası olmayan kümelerde kullanılamaz.
popitem()pop(anahtar) hangi kaydı çıkaracağımızı bildiğimizde uygundur. Son eklenen kaydı anahtarı ve değeriyle birlikte almak için popitem() kullanırız. Sözlük boşsa KeyError verir.
Sözlüğün kendisini de değer olarak tuttuğu listeyi de değiştirebiliriz. copy() yeni bir dış sözlük oluşturur, ancak içteki listeleri ayrıca kopyalamaz. Önceki derste gördüğümüz sığ kopya burada da geçerlidir.
clear()clear() sözlüğü yerinde boşaltır. Aynı sözlüğe başka bir değişken de işaret ediyorsa değişikliği oradan da görürüz. notlar = {} yazmak ise yalnızca notlar adını yeni bir sözlüğe bağlar.
clear() listelerde ve kümelerde de bulunur; bu örnekte sözlük üzerinde kullanıyoruz.
Bir etkinliğe katılan öğrencilerin numaralarını listede saklayabiliriz. Aynı öğrencinin numarası iki kez girilmişse onu bir kez tutmak isteriz. Küme (set), her elemanı en fazla bir kez tutar. Kümenin kendisi değiştirilebilir; elemanların sırası önemli değildir ve kümeye indeksle erişilmez.
set() listeyi kümeye dönüştürürken tekrarları kaldırır. Kümenin yazdırılma sırası değişebilir; çıktı sırasını sabitlemek için örnekte sorted() kullanıyoruz.
Tekrarları kaldırırken numaraların ilk görülme sırasını korumak istiyorsak yalnızca küme kullanamayız. Küme bu sırayı saklamaz.
Dolu küme {...} ile, boş küme set() ile oluşturulur. Boş {} bir sözlüktür. in ile bir öğrencinin kümede olup olmadığını sınarız.
Bir kümeye yalnızca hashlenebilir değerler ekleyebiliriz. Stringler ve sayılar uygundur, listeler değildir.
for kümedeki her değeri bir kez verir. Kümeyi dolaşırken hangi değerin önce geleceğine güvenemeyiz. Sıralı bir çıktı gerekiyorsa sorted() ile yeni bir liste oluştururuz; küme değişmez.
add() bir eleman ekler; aynı eleman zaten varsa küme değişmez. update() verilen koleksiyondaki elemanları tek tek ekler.
remove() ve discard() verilen değeri kümeden çıkarır. Değer kümede yoksa discard() hata vermez; remove() ise KeyError üretir.
Kümede indeks olmadığı için set.pop() hangi elemanı çıkaracağını önceden belirtmemize izin vermez. Belirli bir öğrenciyi silmek istiyorsak remove() veya discard() kullanırız.
remove() ile SilmekBu kez olmayan bir numarayı remove() ile çıkarmayı deneyelim. Kod, KeyError hatası verir. Önceki örnekteki discard(999) ise hatasız çalışmıştı.
“104 numaralı öğrenci bu grupta mı?” sorusunu listede ve kümede aynı in ile sorarız; Python’ın arama yolu farklıdır.
hash(104)
Aday konum: 104
Şema bellek yerleşimini değil, aramanın mantığını gösterir. Hash değerleri çakışabilir; o durumda Python aday elemanları ayrıca karşılaştırır.
n elemanlı bir listede aranan değer sona yakınsa birçok eleman karşılaştırılır: üyelik araması O(n). Küme elemanında ve sözlük anahtarında hash tablosu sayesinde arama ortalama O(1) düzeyindedir. Çok sayıda çakışma olursa en kötü durumda O(n) sürebilir.
Listeyi kümeye dönüştürmek de ortalama O(n) zaman ve ek bellek ister. Tek bir arama için dönüşüm yapmak yerine, çok sayıda üyelik sorgusunda kümeyi tercih etmek daha anlamlıdır. Sözlükte in, değerleri değil anahtarları arar.
Python küme işlemleri, iki gruptaki ortak ve farklı öğrenci numaralarını doğrudan bulur. Örnekte A Python atölyesine, B veri atölyesine katılanların numaralarını tutuyor.
Ortak: 102, 103 · Yalnız A: 101 · Yalnız B: 104
A & B iki grupta da olanları, A | B gruplardan en az birinde olanları verir. Sonuçlar yeni kümelerdir; A ve B değişmez.
A - B yalnız A’da olanları bulur; işlem yönlüdür, B - A farklı sonuç verir. A ^ B ise iki gruptan yalnız birinde olanları seçer.
Bir grup bütünüyle başka bir grupta yer alıyor mu? <= alt küme ilişkisini sınar. Eşit kümeler için de True döner; öz alt küme arıyorsak < kullanırız.
Küme comprehension ile bir koleksiyondaki değerlerden yeni bir küme oluşturabiliriz. Aynı sonucu veren değerler kümede bir kez kalır. Bu örnekte adların başındaki ve sonundaki boşlukları temizleyip onları küçük harfe çeviriyoruz. strip() ve lower() işlemlerini 3. derste görmüştük.
Bu işlem yalnızca harflerin büyüklüğünü ve adların başındaki veya sonundaki boşlukları düzeltir. Farklı kişiler aynı adı taşıyabileceği için bir öğrenciyi yalnızca adıyla tanımlamayız.
Bir sınıfta öğrencilerin notlarını numaralarına göre sözlükte, ödev teslim edenlerin numaralarını ise kümede tutabiliriz. Böylece sözlükten bir öğrencinin notunu bulur, kümeleri karşılaştırarak kimlerin ödevi teslim etmediğini görürüz.
notlar sözlüğünün anahtarları kayıtlı öğrencilerin numaralarıdır. set(notlar) bu numaralardan bir küme oluşturur. Teslim edenlerin kümesini çıkarınca ödevini vermeyenler kalır.
Öğrenci numarası hem sözlük anahtarı hem küme elemanı olabilir. Her iki durumda da aynı numaranın tek bir öğrenciyi tanımladığı varsayılır.
Önceki örneklerde if notlar: ile boş sözlüğü kontrol ettik. Aynı kural listelerde, demetlerde ve kümelerde de geçerli. Şimdi bu davranışı açıkça adlandıralım; ardından bir koleksiyondaki bütün veya herhangi bir koşulu nasıl sınadığımıza bakalım.
if koşulunda bir değeri kullandığımızda Python onu doğru ya da yanlış kabul eder. False, None, sayısal sıfır ve boş string veya koleksiyonlar falsy değerlerdir. Boş olmayan bir koleksiyon ise içindeki elemanlar sıfır ya da False olsa bile truthy kabul edilir.
Bu nedenle if notlar: sözlüğün boş olup olmadığını kontrol eder. if notlar.get("Ada"): ise Ada’nın notu sıfır olduğunda yanıltıcı olabilir; yukarıdaki örnekte karşılaştığımız durum buydu.
all() ve any()Bir listedeki öğrencilerin geçip geçmediğini önce açık bir koşulla hesaplayalım. all() bütün sonuçlar doğruysa, any() en az biri doğruysa True döndürür.
all(notlar) “bütün notlar en az 50” demek değildir. Negatif sayılar da truthy’dir; karşılaştırmayı puan >= 50 gibi açıkça yazmalıyız.
all() boş koleksiyonda True, any() ise False döndürür. Çünkü boş koleksiyonda all() sonucunu bozacak bir eleman da, any() sonucunu doğrulayacak bir eleman da yoktur.
“Herkes geçti” sonucunu boş bir sınıf için raporlamak istemiyorsak önce if notlar: ile veri olup olmadığını kontrol ederiz.
Kodu tamamladıktan sonra çıktının yanı sıra kaynak koleksiyonların değişip değişmediğini de kontrol edin. Beklenen sonucu önce kendiniz tahmin edin.
Bir öğrenci numarasının notu isteniyor. Numara bulunursa notu, bulunmazsa "Kayıt yok" yazdıran notu_goster() fonksiyonunu tamamlayın. Sıfır notu geçerli bir kayıt sayılmalıdır.
Çözüm
if notlar.get(numara) yazsaydık sıfır notunu eksik kayıt sanabilirdik. Burada sınanan notun doğruluk değeri değil, anahtarın varlığıdır.
Bir metin split() ile sözcüklere ayrılmış. Her sözcüğün kaç kez geçtiğini tutan sayac sözlüğünü bir for döngüsüyle oluşturun. Hazır count() metodunu kullanmadan, her sözcüğün mevcut sayısını artırın.
Çözüm
get(sozcuk, 0) ilk karşılaşmada sıfır verir; sonraki karşılaşmalarda sözlükteki sayıyı getirir. Böylece aynı döngü hem yeni anahtarı ekler hem var olan sayıyı artırır.
python ve veri kümelerinden iki atölyeye birden katılanların ve yalnızca Python atölyesine katılanların numaralarını bulun. Çıktıları küçükten büyüğe yazdırın.
Çözüm
Kesişim iki kümede de bulunanları, yönlü fark yalnız soldaki kümede bulunanları verir. sorted() çıktıyı okunur ve karşılaştırılabilir kılar; kaynak kümeleri değiştirmez.
Not sözlüğünde öğrenci numaraları anahtar olarak kullanılıyor. Ödevini teslim etmeyenlerin numaralarını küçükten büyüğe sıralayan eksik_teslimler() fonksiyonunu yazın. Sınıf boşsa boş liste döndürün. Kaynak sözlüğü ve kümeyi değiştirmeyin.
Çözüm
set(notlar) anahtarları alır. Fark işlemi yeni küme, sorted() yeni liste üretir. Boş sözlükte de aynı ifade boş liste döndürür.
anahtar: değer çiftleri tutar. Anahtarlar benzersizdir; [] eksik anahtarda KeyError üretir, get() varsayılan değer döndürebilir.{} boş sözlük oluşturur, len(d) kayıt sayısını verir. d[k] = v değer atar, update() birden çok çifti günceller. setdefault() yalnızca eksik anahtarı ekler; dict.fromkeys() birden çok anahtarı aynı başlangıç değeriyle oluşturur.pop(k) belirtilen anahtarın değerini, popitem() son eklenen (anahtar, değer) çiftini döndürerek siler. clear() sözlüğü boşaltır.for ve keys() anahtarları verir; values() değerleri, items() çiftleri verir. keys() değişiklikleri yansıtan bir görünümdür; reversed(d) anahtarları ters eklenme sırasıyla dolaştırır.set() ile oluşturulur. for elemanları belirsiz sırayla verir, sorted(kume) ise sıralı yeni bir liste üretir.& kesişim, | birleşim, - yönlü fark, ^ simetrik fark üretir; kaynak kümeler değişmez.remove() olmayan elemanda hata verir, discard() vermez. set.pop() belirli bir konumu hedeflemez.all() bütün elemanlar doğruysa, any() en az biri doğruysa True döndürür. Boş koleksiyonda sonuçlar sırasıyla True ve False olur.İleri Programlama – OMÜ | 🏠