Koleksiyonlar, değiştirilebilirlik ve referans sezgisi
Geçen hafta listeyi kısaca gördünüz. Bu hafta listeyle birlikte Python’ın öteki üç koleksiyonunu, yani demeti, sözlüğü ve kümeyi kullanacağız. Koleksiyon, birden çok değeri bir arada tutan yapıdır. Bölümün ağırlığı iki fikirde: değiştirilebilirlik (mutability), yani bir değerin oluşturulduktan sonra değişip değişemeyeceği, ve aynı nesneye referans verme (aliasing), yani iki adın aynı nesneyi göstermesi. Nesne tabanlı programlamaya geçtiğimizde en çok bu iki fikre ihtiyacımız olacak.
Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.
Bu bölümün kapsamı
list,tuple,dictvesetarasından uygun koleksiyonu seçmeb = asonrası iki adın aynı nesneyi göstermesiisile==farkıcopy()ile bağımsız kopya almak, sığ kopyada neyin ortak kaldığı- Demet değişmez olsa da içindeki değiştirilebilir nesnenin değişebilmesi
- Sözlükte olmayan anahtar:
KeyError,inve.get() - Sözlükte
.keys(),.values()ve.items()görünümleri sorted()ile.sort()farkı
copy.deepcopy()ayrıntılarıcollectionsmodülündeki yardımcı yapılar- Küme işlemleri (birleşim, kesişim) ayrıntısı
Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.
Not defterindeki dört işi C’de nasıl yapardınız?
Bir dersin not defterini programa taşıyorsunuz. Defterde her öğrencinin numarası, adı ve notu var. Program şu dört işi yapacak:
- Bir notu değiştirmeden önce defterin yedeğini almak
- Numarası verilen öğrencinin notunu bulmak
- Aynı öğrenciyi iki kez kaydetmemek
- Notları sıralı yazdırmak, ama defterdeki sırayı bozmamak
Programlama Temelleri’nde C ile yazdığınız programları düşünün. Bu işlerin her birini C’de nasıl yapardınız? Okumaya devam etmeden önce her iş için bir satır yazın. Python bilmeniz gerekmiyor: “diziyi döngüyle baştan sona tararım” da bir cevap.
Bölümde bu dört işin Python’da nasıl yazıldığını göreceğiz. Bölümün sonunda listenize döneceğiz.
Liste yerinde değişir
Geçen hafta string’in değiştirilemez (immutable) olduğunu gördünüz: upper() var olan metni değiştirmiyor, yeni bir string döndürüyordu. Liste ise değiştirilebilir (mutable) bir koleksiyondur. append() gibi metotlar yeni bir liste döndürmez, var olan listeyi değiştirir. Buna listeyi yerinde değiştirmek denir. Not defterinin yedeğine de bu yüzden ihtiyaç duyarız: liste değiştikten sonra eski hâli kaybolur.
Hücrede listeye eleman ekleyen ve listeden eleman çıkaran dört metot var. insert(0, 100) listenin başına 100 ekler. remove(85) listedeki ilk 85’i siler. pop() son elemanı listeden çıkarır ve döndürür. Son iki satır ne yazar?
Bir değerin listede olup olmadığını in ile sorarsınız: 90 in scores. Listede olmayan bir değeri remove() ile silmeye çalışırsanız ValueError alırsınız.
Deney: iki ad, bir liste
C’de iki int ya da iki struct değişkeni için b = a yazdığınızda a’daki değer b’ye kopyalanırdı. Python’da aynı satır bir liste için ne yapar? Aşağıdaki kodda yalnız second listesine 30 ekleniyor. İki print() ne yazar?
second = first yeni bir liste oluşturmaz. Python’da = kopya almaz, var olan nesneye bir ad daha bağlar. İki değişken adı aynı liste nesnesini gösterir.
id() her nesneye özgü bir kimlik numarası döndürür. İki adın id() değeri aynıysa ikisi aynı nesneyi gösteriyordur. Aynı soruyu is ile de sorabiliriz. is, iki ifadenin aynı nesneyi gösterip göstermediğini sorar. == ise iki değerin eşit olup olmadığını sorar. Hücredeki son iki satır ne yazar?
Gelecek haftalarda account, customer, order gibi değişkenler de birer nesneyi gösterecek. Bir nesneyi metotla (bu haftaki .append() gibi) değiştirdiğimizde, aynı nesneyi gösteren öteki adlar da yeni durumu görür. Bugünkü liste deneyi bunun küçük bir örneği.
Eşit ama aynı olmayan iki liste
Bu kez a ve b aynı değerlerle ayrı ayrı oluşturuluyor. İki satır ne yazar?
Her köşeli parantezli liste yazımı yeni bir liste nesnesi oluşturur. İçerikleri eşit olan iki liste, iki ayrı nesne olabilir.
Kopya oluşturmak
Not defterinin yedeği ayrı bir liste olmalı. Ayrı bir liste istiyorsak en açık yol .copy() çağırmaktır. Kopyaya 30 ekleniyor. Üç satır ne yazar?
Aynı listeyi list(original) ya da original[:] ile de kopyalayabilirsiniz. Bu derste okuması kolay olduğu için .copy() kullanacağız.
.copy() bir sığ kopya (shallow copy) alır: yalnız dıştaki listeyi kopyalar. Aşağıdaki listenin elemanları da birer liste. Kopyanın ilk iç listesine 99 ekleniyor. original değişir mi?
Sığ kopyada dış liste ayrılır, iç listeler iki kopyada ortak kalır. Bu hafta deepcopy ayrıntısına girmiyoruz. Kopya aldığınızda neyin ayrıldığını, neyin ortak kaldığını kendinize sormayı alışkanlık hâline getirin.
Demet değişmez, içindeki liste değişebilir
Demet (tuple) de liste gibi değerleri sırayla tutar, ama parantezle yazılır ve değiştirilemez. Bir koordinat ya da bir öğrencinin adıyla numarası gibi birlikte kalması gereken değerler için kullanılır. Demetin bir konumuna yeni değer atarsanız TypeError alırsınız:
point = (10, 20)
point[0] = 99 # TypeErrorDemetin içinde liste gibi değiştirilebilir bir nesne durabilir. Aşağıdaki demet bir öğrencinin adını ve notlarını tutuyor. İkinci satır hata verir mi? Vermezse son satır ne yazar?
Demetin hiçbir konumuna yeni değer atanmadı. record[1] hâlâ aynı listeyi gösteriyor, değişen o listenin içeriği. Demet de az önceki iki ad gibi içindeki listeyi gösterir, onu kopyalamaz. Bu yüzden demetin değiştirilemez olması, içindeki listenin de değişmeyeceği anlamına gelmez.
Sözlük: numarayla kaydı bulmak
C’de numarası verilen öğrenciyi bulmak için diziyi baştan sona tarardınız. Python’da bu iş için sözlük (dictionary) kullanılır. Sözlük her anahtarı (key) bir değere (value) bağlar ve süslü parantezle yazılır. Anahtarı verince değere doğrudan ulaşırsınız. Python türü dict’tir.
Aşağıdaki sözlükte anahtarlar öğrenci numarası, değerler not. İkinci satır var olan bir kaydı değiştiriyor. Üçüncü satır sözlükte olmayan bir anahtara değer atıyor. Hücre ne yazar?
Olmayan bir anahtara değer atamak sözlüğe yeni bir kayıt ekler. Var olan anahtara atamak ise eski değeri değiştirir. Bu yüzden bir anahtar sözlükte yalnız bir kez bulunur.
Sözlükte olmayan anahtar
Olmayan bir anahtarı okumaya çalışırsak ne olur? Hücredeki sözlükte 103 yok. Dört satırdan hangisi hata verir, ötekiler ne yazar?
Olmayan bir anahtarı köşeli parantezle okumak KeyError verir. Anahtarın sözlükte olup olmadığını in ile sorarsınız. .get() ise anahtar yoksa hata vermez: None döndürür, ikinci bir değer verdiyseniz onu döndürür.
Sözlüğü dolaşmak
Bir sözlüğü doğrudan for ile dolaşırsanız ne alırsınız: anahtarları mı, değerleri mi? İlk döngü ne yazar?
Sözlüğü doğrudan dolaşmak yalnız anahtarları verir. Anahtarla değeri birlikte kullanmak için .items() yazılır.
.keys()anahtarları verir.values()değerleri verir.items()(anahtar, değer)çiftlerini verir
Bu üç metodun döndürdüğü nesnelere görünüm (view) denir. Hücrenin son satırındaki gibi list() ile listeye çevrilebilirler.
Sözlük anahtarı neden her şey olamaz?
Sözlük ve küme, bir değeri hızlı bulmak için o değerden hesaplanan bir sayı kullanır. Bu sayıya hash denir. Bu yüzden sözlük anahtarının hashlenebilir olması, yani hash değerinin hesaplanabilmesi ve hiç değişmemesi gerekir. Başlangıç için şu kural yeter: değiştirilebilir list, dict ve set nesnelerini anahtar yapmayın:
lookup = {}
lookup[[1, 2]] = "değer" # TypeError: unhashable type: 'list'str, int ve içinde yalnızca değiştirilemez değerler bulunan demetler sık kullanılan anahtarlardır. 12. haftada kendi yazdığımız sınıflarda bu konuya __eq__ metoduyla birlikte döneceğiz.
Küme: her değer bir kez
Aynı öğrenciyi iki kez kaydetmemek için C’de yeni numarayı eklemeden önce diziyi tarardınız. Python’da bu kontrolü küme (set) kendisi yapar: küme her değeri yalnız bir kez tutar. Küme de süslü parantezle yazılır, ama anahtar ve değer çiftleri yerine tek tek değerler içerir. Python türü set’tir.
Hücrede 101 ikinci kez ekleniyor. len(registered) ne yazar?
Kümede sıra yoktur, bu yüzden registered[0] gibi indeksle eleman alamazsınız. Bir değerin kümede olup olmadığını in ile sorarsınız. Bir listedeki tekrarları atmak için set(scores) yazabilirsiniz. Boş küme set() ile oluşturulur, çünkü {} boş bir sözlüktür.
sorted() ve .sort()
Notları sıralı yazdırmak istiyoruz, ama defterdeki sıra bozulmamalı. Python’da adları birbirine benzeyen iki sıralama aracı var. Hücredeki ilk print(scores) ne yazar? Son satır ne yazar?
sorted(scores)yeni bir liste döndürür, verilen listeyi değiştirmez.scores.sort()listeyi yerinde sıralar veNonedöndürür.
Listeyi yerinde değiştiren append(), insert() ve remove() de None döndürür. Defterdeki sırayı korumak istiyorsanız sorted() kullanın. İleride nesnelerden oluşan listeleri sıralarken de bu ayrım geçerli olacak.
Soruya dönelim: dört iş Python’da
Bölümün başında not defteri programının dört işini C’de nasıl yapacağınızı sormuştuk. Python’daki karşılıkları şöyle:
| İş | C’de | Python’da | Dikkat |
|---|---|---|---|
| Yedek almak | Diziyi döngüyle eleman eleman kopyalarsınız | backup = scores.copy() |
backup = scores kopya almaz, iki ad aynı listeyi gösterir |
| Numarayla notu bulmak | Diziyi baştan sona tarar, numarayı karşılaştırırsınız | grades[101] ya da grades.get(101) |
Olmayan anahtarı köşeli parantezle okumak KeyError verir |
| Aynı öğrenciyi iki kez kaydetmemek | Eklemeden önce diziyi tararsınız | registered.add(101) |
Kümede sıra ve indeks yoktur |
| Sıralı yazdırıp sırayı korumak | Diziyi kopyalar, kopyayı sıralarsınız | sorted(scores) |
scores.sort() asıl listeyi sıralar ve None döndürür |
Listenizi tabloyla karşılaştırın. Yedek için “= ile atarım” yazdıysanız C derleyicisi de bunu kabul etmezdi: C’de bir dizi başka bir diziye = ile atanamaz. struct atamasında ise alanlar kopyalanırdı. Python’da = hiçbir türde kopya almaz. Sonraki haftalarda listelerin yerine kendi yazdığımız nesneler gelecek. Tablonun ilk satırındaki kural onlar için de geçerli.
Bölümde gördüğümüz dört koleksiyon yan yana:
| Yapı | Ne tutar | Değiştirilebilir mi? | Bu bölümdeki örnek |
|---|---|---|---|
list |
sıralı değerler | evet | notlar: [70, 85, 90] |
tuple |
sabit sıralı grup | hayır | ad ve notlar: ("Ada", [70, 80]) |
dict |
anahtar → değer | evet | öğrenci numarası → not |
set |
tekrarsız değerler | evet | kayıtlı öğrenci numaraları |
Alıştırma: Doğru koleksiyonu seç
Aşağıdaki her ihtiyaç için list, tuple, dict ve set arasından en uygununu seçin ve nedenini bir cümleyle yazın:
- Bir dersin haftalık konu sırası
- Öğrenci numarasından öğrenci adına hızlı erişim
- Sisteme giriş yapan benzersiz kullanıcı adları
- Değişmemesi gereken
(enlem, boylam)çifti
Tek bir doğru cevap aramayın, kullanım amacına bakın.
Alıştırma: Aliasing hatasını düzelt
Aşağıdaki kodda backup listesinin değişmeden kalması gerekiyor:
backup ayrı bir liste olsun diye tek bir satırı .copy() kullanarak değiştirin.
Alıştırma: Frekans sözlüğü
counts.get(word, 0) ifadesi anahtar yoksa 0 döndürür.
counts[word] = counts.get(word, 0) + 1Sıra sizde: Basit envanter
Ürün kodlarını anahtar, stok miktarlarını değer olarak tutan bir sözlük oluşturun. Ardından:
- Bir ürünün stoğunu azaltın.
- Sözlükte olmayan bir ürünü program hata vermeden sorgulayın.
.items()ile dolaşıp stoğu sıfır olan ürünleri listeleyin.- Ürün kodlarını
sorted()ile sıralayıp yazdırın. - Ürün kodlarının neden benzersiz kaldığını açıklayın.
Son olarak şu soruyu cevaplayın: “Stok sıfırın altına düşmesin” kuralını bu sözlük yaklaşımında nerede koruyoruz? Bu sorunun cevabı, ileride sınıf metotlarına neden ihtiyaç duyacağımızı gösterecek.
Çalışır kod görevi: Envanter programını üç durumla çalıştırın: sözlükte olan bir ürün, olmayan bir ürün ve stoğu sıfır olan bir ürün. Üç durum da çıktıda görünmeli.
Tek sayfa özet
- Liste değiştirilebilir.
append(),insert(),remove(),pop()vesort()listeyi yerinde değiştirir. b = akopya almaz. İki ad aynı nesneyi gösterir ve bir addan yapılan değişiklik ötekinden de görülür.isiki adın aynı nesneyi gösterip göstermediğini,==değerlerin eşit olup olmadığını sorar..copy()dış listeyi ayırır, içteki nesneler iki kopyada ortak kalır.- Demet değiştirilemez, ama içindeki liste değişebilir.
- Sözlük her anahtarı bir değere bağlar. Olmayan anahtarı köşeli parantezle okumak
KeyErrorverir. Önceinile sorun ya da.get()kullanın. - Sözlüğü anahtar ve değerle birlikte dolaşmak için
.items()kullanılır. - Sözlük anahtarları hashlenebilir olmalıdır. Liste anahtar olamaz.
- Küme her değeri bir kez tutar. Kümede sıra ve indeks yoktur.
sorted()yeni liste döndürür..sort()listeyi yerinde sıralar veNonedöndürür.- Sözlük programın her yerinden güncellenirse “stok sıfırın altına düşmesin” gibi bir kuralı korumak zorlaşır. İleride sınıf ve metot yazmamızın bir nedeni bu.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
list,tuple,dictvesetarasından probleme uygun koleksiyonu seçer.- Demetin elemanlarına yeni değer atanamadığını, ama içindeki değiştirilebilir nesnenin değişebildiğini açıklar.
b = asonrasında iki adın aynı nesneyi gösterdiğini tanır ve bir addan yapılan değişikliğin ötekinden de görüleceğini öngörür.isile==karşılaştırmalarını birbirinden ayırır.copy()ile bağımsız kopya oluşturur ve sığ kopyada neyin ortak kaldığını belirler.- Sözlükte
.keys(),.values()ve.items()görünümlerini uygun durumda kullanır. sorted()ile.sort()arasındaki farkı dönüş değeri ve listeyi yerinde değiştirme açısından gösterir.- Sözlükte olmayan bir anahtarı
inya da.get()ile hata vermeden sorgular.