Koleksiyonlar, değiştirilebilirlik ve referans sezgisi
Nesne tabanlı programlamaya geçmeden önce iki şeyi bilmemiz gerekiyor: Python’da birden fazla değer nasıl tutulur ve bir değişken adı bir değeri nasıl gösterir? Bu hafta liste, demet, sözlük ve kümeyi kısaca kullanacağız. En çok üç konu üzerinde duracağız: değiştirilebilirlik (mutability), yani bir değerin oluşturulduktan sonra değişip değişemeyeceği; aynı nesneyi paylaşma (aliasing), yani iki adın aynı nesneyi göstermesi; ve kopyalama. Dört koleksiyonun bütün ayrıntılarını tek haftada ezberlemeniz beklenmiyor.
Bu bölümün kapsamı
NoteSınavda sorulur
list, tuple, dict ve set arasından uygun koleksiyonu seçme
Demet değişmez olsa da içindeki değiştirilebilir nesnenin değişebilmesi
b = a sonrası iki adın aynı nesneyi göstermesi
is ile == farkı
copy() ile bağımsız kopya almak; sığ kopyada neyin ortak kaldığı
Sözlükte .keys(), .values() ve .items() görünümleri
sorted() ile .sort() farkı
NoteSınav dışı (bilgi amaçlı)
copy.deepcopy() ayrıntıları
collections modü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; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
İki değişken aynı listeyi ne zaman gösterir?
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 iki liste adı için aynı satır kopya almaz: iki ad aynı listeyi gösterir ve birinden yaptığınız değişikliği öteki de görür. Bu bölümde koleksiyon türlerini ezberlemekten çok bu durumu inceleyeceğiz. Nesne tabanlı programlamaya geçtiğimizde en çok bu fikre ihtiyacımız olacak.
NoteBu haftanın önceliği
Ders süresi yetmezse set ayrıntılarını ve iç içe listelerle sığ kopya örneklerini sonraya bırakabiliriz. Sonraki haftalar için şunu iyice kavrayın: iki ad aynı değiştirilebilir nesneyi gösterebilir ve bir addan yapılan değişiklik ötekinden de görülür.
Dört temel koleksiyon
Yapı
Temel fikir
Değiştirilebilir?
Tipik kullanım
list
sıralı değerler
evet
notlar, ürünler, adımlar
tuple
sabit sıralı grup
hayır
koordinat, değişmez kayıt parçası
dict
anahtar → değer
evet
öğrenci no → öğrenci bilgisi
set
benzersiz değerler
evet
üyelik, tekrarları kaldırma
Kümede "python" iki kez yazılsa bile yalnızca bir kez tutulur.
Liste değişir, demet değişmez
Aynı işlemi bir tuple’da denerseniz TypeError alırsınız:
point = (10, 20)point[0] =99# hata
Tuple’ın değiştirilemez olması şu anlama gelir: point[0] gibi bir konuma sonradan başka bir değer atanamaz. Ama tuple’ın içinde liste gibi değiştirilebilir bir nesne durabilir:
Tuple’ın hiçbir konumuna yeni değer atamadık; tuple’ın içindeki listeye bir eleman ekledik. Demek ki “immutable (değiştirilemez)” sözcüğü “içindeki her şey de değişmez” anlamına gelmez.
Demet içinde değiştirilebilir nesne
Deney: iki ad, bir liste
Aşağıdaki kodu çalıştırmadan önce çıktıyı tahmin edin.
second = first yeni bir liste oluşturmaz. İki değişken adı aynı liste nesnesini gösterir.
Aliasing ve kopya
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 sayılıp sayılmadığını sorar.
ImportantBu fikir OOP’de sürekli karşımıza çıkacak
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
İçerikleri eşit, ama bunlar iki ayrı liste nesnesi.
Kopya oluşturmak
Ayrı bir liste istiyorsak en açık yollardan biri .copy() çağırmaktır:
Aynı dış listeyi list(original) veya original[:] ile de kopyalayabilirsiniz; bu derste okuması kolay olduğu için .copy() kullanacağız.
.copy() bir sığ kopya (shallow copy) alır: yalnızca dıştaki listeyi kopyalar. İç içe listelerde içteki listeler iki kopya arasında hâlâ ortaktır:
Bu hafta deepcopy ayrıntısına girmiyoruz. Yine de kopya aldığınızda neyin ayrıldığını, neyin ortak kaldığını kendinize sormayı alışkanlık hâline getirin.
Sözlükle kayıt tutmak ve dolaşmak
Bir sözlüğü doğrudan for ile dolaşırsanız yalnızca anahtarları alırsınız. Anahtarla değeri birlikte kullanmak için çoğu zaman .items() yazılır:
.keys() anahtarları,
.values() değerleri,
.items()(anahtar, değer) çiftlerini verir.
Bu üç metodun döndürdüğü nesnelere görünüm (view) denir. Yukarıdaki 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:
str, int ve içinde yalnızca değiştirilemez değerler bulunan tuple’lar sık kullanılan anahtarlardır. 12. haftada kendi yazdığımız sınıflarda bu konuya __eq__ metoduyla birlikte döneceğiz.
sorted() ve .sort()
İki isim benzer, etkileri farklıdır:
sorted(iterable) yeni bir liste döndürür; verilen koleksiyonu değiştirmez.
list.sort() mevcut listeyi yerinde değiştirir ve None döndürür.
İleride nesnelerden oluşan listeleri sıralarken de bu ayrım geçerli olacak.
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.
Ürün kodlarını anahtar, stok miktarlarını değer olarak tutan bir sözlük oluşturun. Ardından:
Bir ürünün stokunu azaltın,
Sözlükte olmayan bir ürünü program hata vermeden sorgulayın,
.items() ile dolaşıp stoku 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 stoku sıfır olan bir ürün. Üç durum da çıktıda görünmeli.
Sınav provası
Önce kendi cevabınızı seçin; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu okuyun. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.
Madde 1. Bir metindeki benzersiz kelimeleri tutmak için en uygun koleksiyon hangisidir?
A.tuple; değiştirilemez olduğu için tekrar eklenemez
B.set; aynı değeri yalnız bir kez tutar
C.list; sıra korunduğu için tekrar sorunu oluşmaz
D.str; kelimeler zaten metin olduğu için
E.dict; anahtarlar sıralı tutulduğu için
Madde 2. Aşağıdaki program ne yazdırır?
record = ("Ada", [70, 80])record[1].append(90)print(record)
A.('Ada', [70, 80, 90]); demetin içindeki liste değişir
B.('Ada', [70, 80], 90)
C.TypeError; demet değiştirilemez
D.('Ada', [70, 80]); demet içindeki liste kopyalanır
A. Hata verir; sort() dönüş değeri bir değişkene atanamaz
B.[80, 60, 90]
C.None; list.sort() listeyi yerinde sıralar ve değer döndürmez
D.[60, 80, 90]
E.True
TipCevaplar ve gerekçeler
Madde 1. Doğru: B. Küme tekrarları kendiliğinden eler ve “bu kelime var mı?” sorusuna doğrudan cevap verir. Sıra önemli değilse en doğrudan seçimdir.
A yanlış: Değiştirilemezliği “tekrar eklenemez” diye okumak kolaydır. Oysa değişmezlik tekrar içermediği anlamına gelmez; ("a", "a") geçerlidir.
C yanlış: Liste en tanıdık koleksiyon olduğu için ilk akla gelen seçimdir. Ama liste aynı değeri birden çok kez tutar; benzersizliği elle sağlamak gerekir.
D yanlış: Kelimeler zaten metin olduğu için ayrı bir yapıya gerek yokmuş gibi görünür. Ama metin, kelimeleri ayrı öğeler olarak tutan bir koleksiyon değildir.
E yanlış: Sözlük anahtarları da tekil olduğu için bu seçim işe yarar gibi görünür. Ama gerekçe yanlıştır: benzersizliği sıra değil, anahtarların tekil olması sağlar. Değer tarafı da boşa gider; yalnız üyelik için küme yeterlidir.
Madde 2. Doğru: A. Demetin elemanları değişmedi, içindeki listenin durumu değişti. Demetin değişmez olması, elemanlarına yeni değer atanamayacağı anlamına gelir. record[1] hâlâ aynı listeyi gösterir; o listenin içeriği ise değiştirilebilir.
B yanlış:append “sona ekle” diye okunduğunda değer demetin sonuna eklenmiş gibi görünür. Ama append demete değil, demetin içindeki listeye uygulanır.
C yanlış: Demetin değiştirilemez olduğunu bilen öğrenci her değişikliğin hata vereceğini bekler. Oysa hata ancak record[1] = ... gibi bir atamada çıkar; burada demetin elemanına yeni değer atanmıyor.
D yanlış: Demetin değişmezliği, içindeki listeyi korumak için kopyaladığı izlenimini verir. Oysa demet elemanlarını kopyalamaz; nesnelere referans tutar.
E yanlış: Demetin metotları kısıtlı olduğu için elemanlarının da kısıtlı olduğu düşünülebilir. Ama eleman bir listedir; listenin metotları çağrılabilir.
Madde 3. Doğru: D.second = first yeni bir liste oluşturmaz; iki ad aynı liste nesnesini gösterir. Bir addan yapılan değişiklik diğerinden de görülür.
A yanlış: Ekleme second adıyla yapıldı, ama iki ad aynı listeyi gösterdiği için iki satır da aynı çıkar.
B yanlış: Atama kopya alsaydı böyle olurdu; oysa atama aynı listeye yalnızca ikinci bir ad verir.
C yanlış:append listeyi sıfırlamaz; sonuna ekler.
E yanlış: Aynı nesneye birden çok ad bağlamak geçerli ve yaygındır.
Madde 4. Doğru: A. Python argüman olarak verilen nesneyi fonksiyona kopyalamaz. Fonksiyon içindeki liste ile dışarıdaki sayilar aynı nesneyi gösterir.
B yanlış: Python argümanı kopyalamaz. Bu beklenti, C’deki “fonksiyona değerin kopyası gider” alışkanlığından gelir.
C yanlış: Fonksiyon bir şey döndürmediği için None akla gelir. Ama yazdırılan, fonksiyonun dönüş değeri değil, sayilar listesidir.
D yanlış: Fonksiyonun dışarıdaki veriye dokunmaması gerektiği sık söylendiği için bunun bir kural olduğu sanılabilir. Böyle bir kısıt yoktur; fonksiyon çağıranın listesini değiştirebilir. Bu yan etkinin istenip istenmediğine fonksiyonu yazan karar verir.
E yanlış: Akılda yalnız eklenen değer kalınca bu sonuç seçilebilir. Ama append listeyi sıfırlamaz; sona ekler.
Madde 5. Doğru: E. İçerikleri eşittir ama bunlar iki ayrı liste nesnesidir. == değeri, is kimliği sorgular.
A yanlış: Listelerde == eleman eleman karşılaştırır ve eşitlik doğrudur.
B yanlış:is her nesne için geçerlidir.
C yanlış: İçeriğin eşit olması, iki listenin aynı nesne olduğunu göstermez.
D yanlış: Sonuçlar ters yazılmış.
Madde 6. Doğru: C.copy() yeni bir liste nesnesi oluşturur; iki ad artık ayrı nesneleri gösterir.
A yanlış:copy anahtar sözcük değildir; ad olarak kullanılabilir.
B yanlış: Kopya kaynağın elemanlarıyla başlar; boş değildir.
D yanlış: Ekleme copy listesine yapıldı; değişen odur.
E yanlış: Bu çıktı copy = original yazılsaydı görülürdü; copy() ise ayrı bir liste oluşturur.
Madde 7. Doğru: E.copy() yalnız bir düzey kopyalar: sığ kopya dış listeyi ayırır, içteki nesneler paylaşılır. copy[0] ile original[0] aynı iç listeyi gösterir.
A yanlış:copy() adı tam bir kopya alındığı izlenimini verir. Bu ancak derin kopya alınsaydı doğru olurdu.
B yanlış: Kopyanın asıl listeyi korumak için kilitlendiği düşünülebilir. Ama kopyalama içteki listeleri değiştirilemez yapmaz.
C yanlış:append çağrısı copy üzerinden yapıldığı için eklemenin dış listeye gittiği sanılabilir. Ama ekleme dış listeye değil, ilk iç listeye yapılır.
D yanlış:append yeni bir liste kuruyormuş gibi okunabilir. Ama append iç listeyi sıfırlamaz; sonuna ekler.
Madde 8. Doğru: B. Sözlük üzerinde doğrudan dolaşmak anahtarları verir. (anahtar, değer) çiftleri için .items() gerekir.
A yanlış: Her tur tek bir anahtar verir; anahtar ve değer dönüşümlü gelmez.
C yanlış: Bu .items() davranışıdır.
D yanlış: Sözlük for ile dolaşılabilir.
E yanlış: Değerler için .values() yazılmalıdır.
Madde 9. Doğru: D.sorted() yeni bir liste döndürür ve kaynağı değiştirmez.
A yanlış:None döndüren list.sort()tur; sorted() yeni liste verir.
B yanlış: Dönüş değeri normal bir listedir ve atanabilir.
C yanlış:ordered sıralı sonucu tutar.
E yanlış: Bu scores.sort() çağrılsaydı olurdu; sorted() yerinde değiştirmez.
Madde 10. Doğru: C. Listeyi yerinde değiştiren metotlar None döndürür. Sıralanmış listeye scores adıyla ulaşılır.
A yanlış: Atama geçerlidir; atanan değer Nonedır.
B yanlış: Liste sıralanır, ama dönüş değeri liste değildir.
D yanlış: Bu sorted(scores) sonucudur.
E yanlış: Sıralamanın başarılı olduğunu bildiren bir değer döndürmez.
Tek sayfa özet
Her koleksiyonun kendi kullanım alanı var. Bu hafta hepsini aynı ayrıntıda ezberlemek yerine hangisini ne zaman seçeceğinizi öğreniyoruz.
Değiştirilebilir nesnelerde iki değişken aynı nesneyi gösterebilir.
is iki adın aynı nesneyi gösterip göstermediğini, == değerlerin eşit olup olmadığını sorar.
.copy() dış koleksiyonu ayırır; içteki nesneler paylaşılabilir.
Sözlüğü anahtar ve değerle birlikte dolaşmak için .items() kullanılır.
Sözlük anahtarları hashlenebilir olmalıdır.
sorted() yeni liste döndürür; .sort() listeyi yerinde değiştirir.
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, dict ve set arası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 = a sonrası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.
is ile == 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.