Listeler, demetler ve referans sezgisi
Şimdiye kadar bir değişkende çoğunlukla tek bir değer tuttuk. Gerçek programlarda ise bir öğrencinin sınav puanları, bir ürünün haftalık satışları ya da bir sınıftaki adlar gibi birden fazla değeri birlikte saklamak gerekir.
Bu hafta Python’ın iki temel koleksiyonunu (birden fazla değeri bir arada tutan yapı) öğreneceğiz: liste (list) ve demet (tuple). Ayrıca iki değişken aynı listeyi gösterdiğinde ne olduğuna bakacağız. Bu durum, listenin içeriği sonradan değiştirilebildiği için ortaya çıkar.
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ı
- İndeks ve dilimle liste elemanlarına erişim
append(),remove()veindavranışlarısort()ilesorted()farkı- Liste ile demet ayrımı ve fonksiyondan dönen demeti açma
- Aynı nesnenin paylaşılması ve bağımsız kopya oluşturma
- Üzerinde dolaşılan listeyi aynı anda değiştirmenin riski
- Boş listenin ortalama ve maksimum hesabında uç durum olması
- Liste kavraması (list comprehension)
- Tek elemanlı demetin
(5,)biçiminde yazılması copy.deepcopy()ile derin kopyalama
Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.
Puan kâğıdıyla dört işi nasıl yaparsınız?
Bir dersin sınav puanları tek bir kâğıtta, öğrenci numarası sırasıyla alt alta yazılı. 60’ın altında kalan öğrenci bütünlemeye girecek. Bu kâğıtla dört iş yapılacak:
Puanları büyükten küçüğe dizip en yüksek üç puanı ilan etmek. Kâğıttaki numara sırası daha sonra yine lazım.
60’ın altındaki puanları kâğıttan silip bütünleme listesine yazmak.Bir asistan kâğıttaki puanlar üzerinde deneme hesapları yapacak, bazı puanların üstünü çizip yenisini yazacak. Asıl puanlar bozulmamalı.
Sınıf ortalamasını ve en yüksek puanı birlikte duyurmak.
bölümde bir işin adımlarını sözde kodla yazmıştık. Bu dört işin adımlarını da öyle, Türkçe ve madde madde yazın. Sonra adımlarınızı şu durumlarla deneyin:
- Kâğıdı sıraya dizerken üstüne mi yazıyorsunuz? Numara sırası ne oluyor?
- Kâğıt bir telefon uygulamasındaki liste olsun: bir satırı silince alttaki satırlar bir yukarı kayıyor. Puanlar sırasıyla
45,40,70. Adımlarınız “satıra bak,60’ın altındaysa sil, sıradaki satıra geç” diyorsa40’a ne olur? - Asistana kâğıdın kendisini verdiniz. Asistan bir puanın üstünü çizdi. Sizin elinizdeki puanlar hangisi?
- Sınava kimse girmedi, kâğıt boş. Ortalama adımınız ne yapıyor?
Python bilmeniz gerekmiyor: “Asistana fotokopi veririm” de bir adım. Adımlarınızı bir kenara yazın. Bölümde her birinin Python’da nasıl yazıldığını göreceğiz. Alıştırmalardan önce listenize döneceğiz.
Liste oluşturmak
Bir liste köşeli parantezlerle oluşturulur:
puanlar = [70, 85, 92, 60]Liste değerleri belirli bir sırayla tutar. Listedeki her değere eleman, elemanın sıradaki konumuna indeks denir:
Değer: 70 85 92 60
İndeks: 0 1 2 3
String’lerde olduğu gibi indeksler 0’dan başlar. Negatif indeks sondan sayar: -1 son elemandır. Üç satır ne yazar?
Liste dilimleme
- bölümde string’lerde kullandığımız dilimleme söz dizimi listelerde de çalışır. Başlangıç dâhil, bitiş hariçtir. Üç satır ne yazar?
puanlar[1:4] indeks 1, 2 ve 3’teki elemanları alır: [85, 92, 60]. puanlar[:3] baştan başlar, indeks 3’e gelmeden durur: [70, 85, 92]. puanlar[-2:] sondan ikinci elemandan sona kadar gider: [60, 78].
String dilimlemeden bir farkı var: sonuç string değil, yeni bir liste olur.
Liste elemanını değiştirmek
- bölümde string’lerin değiştirilemez olduğunu gördük: bir string’in tek bir harfini yerinde değiştiremezsiniz. Listeler ise değiştirilebilir (mutable): bir elemanını yerinde değiştirebilirsiniz. Hücre ne yazar?
Burada yeni bir liste oluşturulmadı. Var olan listenin ilk elemanı değişti.
Liste indeksi de listenin sınırları içinde kalmalıdır. Üç elemanlı bir listede geçerli pozitif indeksler 0, 1 ve 2’dir. puanlar[3] yazarsanız Python IndexError verir.
append(), remove() ve in
Listenin sonuna yeni eleman eklemek için append() kullanılır. Belirli bir değeri listeden kaldırmak için remove() kullanılır. İki print() ne yazar?
remove() ilk eşleşen değeri siler. Değer listede yoksa Python ValueError verir. Bu yüzden silmeden önce değerin listede olup olmadığına bakabilirsiniz:
if "kalem" in urunler:
urunler.remove("kalem")in, bir değerin listede olup olmadığını denetler ve True ya da False verir. İki satır ne yazar?
append() ve remove() yeni bir liste döndürmez, var olan listeyi değiştirir ve None döndürür. Bu yüzden liste = liste.append(x) yazmayın: liste değişir ama liste adı artık None değerini gösterir.
Tahmin et: liste nasıl değişir?
Aşağıdaki kodu çalıştırmadan önce her print() satırının çıktısını tahmin edin:
Sonra çalıştırıp tahmininizi karşılaştırın.
Alıştırma: eksik puanı ekleyin
Bir öğrencinin üç sınav puanı listede tutuluyor. Dördüncü puan olan 88 değerini listeye ekleyin.
Beklenen sonuç:
[72, 80, 91, 88]
puanlar.append(88) yazabilirsiniz.
puanlar = [72, 80, 91]
puanlar.append(88)
print(puanlar)Liste üzerinde for ile gezinmek
Bir listedeki her elemanı sırayla işleyebiliriz. Döngünün her turunda puan, listenin sıradaki değerini alır. İkinci hücre ne yazar?
Listede gezinmek için çoğu zaman range() gerekmez. Yalnızca elemanlar lazımsa for ile doğrudan listede dolaşmak daha okunur.
Dolaştığınız listeden aynı döngüde eleman silmeyin
Aşağıdaki kod 60’ın altındaki puanları listeden silmeye çalışıyor. Döngü bitince listede ne kalır?
Çıktı [40, 70] olur, 40 silinmeden kalır. İlk turda döngü indeks 0’daki 45’e bakar ve onu siler. 40 ile 70 birer konum öne kayar: 40 artık indeks 0’da. İkinci turda döngü indeks 1’e, yani 70’e geçer. 40’a hiç bakılmaz.
Döngü sürerken listenin uzunluğu ve elemanların konumu değişir, bu yüzden bazı elemanlar hiç işlenmeden atlanabilir. Python bu durumda hata vermez. Daha güvenli yol, tutmak istediğiniz değerleri yeni bir listeye eklemektir. Bunu birazdan “Listeye koşullu eleman eklemek” başlığında göreceğiz.
Ortalama hesaplamak
Bir listenin ortalaması için önce toplamı, sonra eleman sayısını kullanabiliriz. Hücre ne yazar? Sonra puanlar = [] yapıp yeniden çalıştırın.
Python’da toplamı veren sum() gibi hazır fonksiyonlar da var. Yine de bu aşamada toplamı döngüyle hesaplıyoruz, çünkü böylece biriktiricinin (her turda güncellenen sonucu tutan değişken) nasıl çalıştığını görürsünüz.
Boş listenin eleman sayısı 0’dır. toplam / len(puanlar) ifadesi boş listede ZeroDivisionError (sıfıra bölme hatası) verir. Boş liste burada bir uç durumdur (edge case): sıradan girdilerden farklı davranan, kolay gözden kaçan sınırdaki girdi.
Minimum ve maksimum
Python listelerinde en küçük ve en büyük değeri doğrudan bulmak için min() ve max() kullanılabilir. İki satır ne yazar?
Yine de en büyük değeri döngüyle bulmayı (maksimum arama kalıbı) bilmeniz gerekir. İleride “puanı en yüksek öğrencinin adı” gibi sorularda sayının yanında o puanın kime ait olduğunu da bulmamız gerekecek.
Boş olmayan bir liste için ilk elemanı başlangıç kabul edebiliriz:
Adım adım:
- İlk değeri “şimdiye kadarki en yüksek” kabul et.
- Kalan her değeri bununla karşılaştır.
- Daha yüksek değer bulursan
en_yuksekdeğerini güncelle.
Bu kalıp listenin boş olmadığını varsayar. Boş listede puanlar[0] IndexError, max(puanlar) ValueError verir. Fonksiyonu yazarken “liste boş gelirse ne döndürecek?” sorusunu da cevaplamalı ve bunu fonksiyonun sözleşmesine (7. bölüm) yazmalısınız. 11. bölümde kayıtlarla çalışırken boş liste için None döndüreceğiz.
Sıralama: sort() ve sorted()
Bir listeyi küçükten büyüğe sıralamanın iki yolu vardır. sort() var olan listeyi değiştirir. sorted() ise asıl listeye dokunmaz, yeni bir sıralı liste döndürür. İki hücrede puanlar neyi gösterir?
sort() sonuç olarak bir liste döndürmez, None döndürür. yeni = puanlar.sort() yazarsanız liste sıralanır ama yeni None olur.
Ters sıralama için reverse=True yazılır:
print(sorted(puanlar, reverse=True))Sıralama algoritmalarını bu derste kendimiz yazmayacağız, onları Veri Yapıları ve Programlama dersinde ayrıntılı göreceğiz. Burada var olan listeyi sıralamakla yeni bir sıralı liste almak arasındaki farkı bilmeniz yeterli.
Alıştırma: geçen puanları sayın
Aşağıdaki listede 60 ve üzeri kaç puan olduğunu bulun.
Beklenen sonuç 3’tür.
if puan >= 60: koşulu içinde sayacı bir artırın.
puanlar = [45, 60, 72, 38, 90, 59]
gecen_sayisi = 0
for puan in puanlar:
if puan >= 60:
gecen_sayisi = gecen_sayisi + 1
print(gecen_sayisi)Listeye koşullu eleman eklemek
Bir listedeki bazı değerlerden yeni bir liste oluşturabiliriz. Aşağıdaki kod asıl listeye dokunmadan puanları iki yeni listeye ayırıyor. İki satır ne yazar?
Buna filtreleme denir: yalnızca koşulu sağlayan elemanlar yeni listeye eklenir. Döngü asıl listeyi değiştirmediği için hiçbir eleman atlanmaz.
Demet (tuple)
Demet de liste gibi değerleri belirli bir sırayla tutar, ama oluşturulduktan sonra elemanları değiştirilemez. Demet köşeli parantezle değil, normal parantezle yazılır. İki satır ne yazar?
Bir demetin elemanına yeniden değer atamaya çalışırsak ne olur?
Python TypeError: 'tuple' object does not support item assignment hatası verir. Demetin elemanı okunabilir ama değiştirilemez.
Bu yüzden seçim şöyle yapılır:
- Liste: içerik zamanla değişecekse.
- Demet: birkaç değer birlikte küçük ve sabit bir grup oluşturuyorsa.
Örneğin bir rengin RGB bileşenleri (kırmızı, yeşil, mavi) ya da bir konumun enlem ve boylamı demette tutulabilir.
Demet açma ve fonksiyondan birden fazla değer döndürme
Demetteki değerleri ayrı değişkenlere alabiliriz. Buna demet açma (tuple unpacking) denir. Hücre ne yazar?
return satırında değerleri virgülle ayırırsak Python bunları tek bir demette toplar. Hücredeki dört print() ne yazar?
Şu iki return aynı demeti döndürür:
return 0, 100
return (0, 100)Çağıran tarafta, son iki satırdaki gibi, demeti doğrudan ayrı adlara açabiliriz.
Aynı listeye referans verme
Python’da her liste bellekte bir nesnedir (Python’ın bellekte tuttuğu bir değer). Değişken adı bu nesneyi gösterir. Listeler değiştirilebilir olduğu için şu davranışa dikkat etmek gerekir. İki satır ne yazar?
İki satır da [10, 20, 30, 40] yazar. liste1’e hiç dokunmadık ama o da değişti.
liste2 = liste1 ataması ikinci bir liste oluşturmaz. İki değişken bellekteki aynı liste nesnesini gösterir. Bu duruma aynı nesneye referans verme (aliasing) denir. Asistana puan kâğıdının fotokopisini değil, kâğıdın kendisini vermek gibidir.
Bunu şöyle düşünebilirsiniz:
liste1 ─┐
├──> [10, 20, 30, 40]
liste2 ─┘
liste2 ile yaptığınız değişiklik bu tek listeyi değiştirir. Bu yüzden liste1’i yazdırdığınızda da aynı değişikliği görürsünüz.
Tahmin et: iki değişken mi, iki liste mi?
Çalıştırmadan önce çıktıyı tahmin edin:
Sonucu görünce “Python a’yı gizlice değiştirdi” diye düşünmeyin. Ortada tek bir liste var ve iki ad da onu gösteriyor.
Bağımsız kopya oluşturmak
Bağımsız bir liste gerekiyorsa .copy() kullanabiliriz. Bu kez iki satır ne yazar?
İlk satır [1, 2, 3], ikinci satır [1, 2, 3, 4] yazar. .copy() yeni bir liste nesnesi oluşturdu. 4 yalnız kopyaya eklendi.
Tam dilim ([:], başı ve sonu boş bırakılmış dilim) de listenin kopyasını verir: kopya = orijinal[:]. Bu derste çoğunlukla .copy() kullanacağız, çünkü kopya istediğimizi daha açık gösterir. Başka kaynaklarda liste[:] görürseniz onun da aynı işi yaptığını bilin.
İkisi de sığ kopya (shallow copy) oluşturur: liste yenidir ama içindeki elemanlar kopyalanmaz, iki liste aynı elemanları gösterir. İçinde başka liste olmayan (tek seviyeli) bir listede bu ayrıntı sorun çıkarmaz. İç içe listelerin kopyalanmasına bu derste girmiyoruz.
Alıştırma: bağımsız çalışma listesi
Aşağıdaki kodda taslak listesine yapılan değişiklik orijinal listeyi de değiştiriyor. taslak için bağımsız bir kopya oluşturun.
Beklenen:
Orijinal: ['A', 'B', 'C']
Taslak: ['A', 'B', 'C', 'D']
taslak = orijinal.copy() yazın.
orijinal = ["A", "B", "C"]
taslak = orijinal.copy()
taslak.append("D")
print("Orijinal:", orijinal)
print("Taslak:", taslak)Fonksiyona liste vermek
Bir fonksiyona argüman olarak liste de verebiliriz. Hücre ne yazar?
Fonksiyon listeyi yalnızca okuyorsa ne yaptığını izlemek kolaydır. Listeyi değiştiren bir fonksiyon ise çağıran koddaki listeyi de değiştirir, çünkü fonksiyon listenin kopyasıyla değil kendisiyle çalışır. Aşağıdaki hücrede notlar değişir mi?
[0, 85] yazar. Fonksiyon çağrılınca puanlar parametresi notlar ile aynı listeyi gösterir, b = a atamasındaki gibi. Fonksiyonun içindeki değişiklik o tek listeyi değiştirir.
Bir fonksiyonu kullanan kişi, ona verdiği listenin değişip değişmeyeceğini bilmelidir. Hesap yapan fonksiyonlarda listeyi yalnızca okuyun. Yeni bir sonuç gerekiyorsa onu döndürün.
Soruya dönelim: puan kâğıdı Python’da
Bölümün başında puan kâğıdıyla yapılacak dört işin adımlarını yazıp birkaç durumla denemiştiniz. Python’daki karşılıkları şöyle:
| İş | Sözde kodda | Python’da | Dikkat |
|---|---|---|---|
| En yüksek üç puanı ilan etmek, numara sırasını korumak | “Puanları büyükten küçüğe başka bir kâğıda diz, ilk üçünü oku” | sirali = sorted(puanlar, reverse=True), sonra sirali[:3] |
puanlar.sort() asıl listeyi sıralar, numara sırası kaybolur. sort() yeni liste döndürmez, None döndürür |
60’ın altındakileri bütünleme listesine almak |
“Her satıra bak, 60’ın altındaysa bütünleme listesine yaz” |
Yeni listeler: butunleme.append(puan), gecenler.append(puan) |
Dolaşılan listeden remove() ile silmek kayan elemanı atlar: 45, 40, 70’te 40 kalır |
| Asistanın asıl puanları bozmaması | “Asistana fotokopi ver” | taslak = puanlar.copy() |
taslak = puanlar kâğıdın kendisini vermektir: asistanın değişikliği asıl listede de görünür |
| Ortalama ve en yüksek puanı birlikte duyurmak | “Kâğıt boşsa duyuru yapma. Değilse ortalamayı ve en yüksek puanı bul” | if len(puanlar) == 0: kontrolü, sonra return ortalama, en_yuksek ve ort, eb = ozet(puanlar) |
Boş listede bölme ZeroDivisionError, max() ValueError verir. İki değer tek bir demet olarak döner |
Listenizi tabloyla karşılaştırın. “Satıra bak, sil, sıradaki satıra geç” yazdıysanız kâğıtta doğru çalışan adım, kayan listede bir elemanı atlar. “Asistana kâğıdı veririm” yazdıysanız Python’daki = de bunu yapar: kopya almaz, aynı listeye ikinci bir ad verir. Boş kâğıdı düşünmediyseniz program da düşünmez: bölme hatası verir.
Üret: puan özetini hesaplayın
Bölümün başındaki dördüncü işi bir fonksiyonla yapın. puan_ozeti(puanlar) adlı fonksiyon:
- Toplamı döngüyle hesaplamalı,
- Ortalamayı bulmalı,
60ve üzeri puanların sayısını hesaplamalı,- Sonucu
(ortalama, gecen_sayisi)biçiminde bir demet olarak döndürmeli, - Liste boşsa bölme yapmadan
(None, 0)döndürmeli.
Başlangıç:
Beklenen sonuç:
(58.4, 2)
(None, 0)
Önce if len(puanlar) == 0: ile boş listeyi ayırın. Sonra toplam ve 60 üzeri sayaç için iki ayrı biriktirici kullanın. Ortalama toplam / len(puanlar) olur.
def puan_ozeti(puanlar):
if len(puanlar) == 0:
return None, 0
toplam = 0
gecen_sayisi = 0
for puan in puanlar:
toplam = toplam + puan
if puan >= 60:
gecen_sayisi = gecen_sayisi + 1
ortalama = toplam / len(puanlar)
return ortalama, gecen_sayisi
print(puan_ozeti([45, 40, 70, 82, 55]))
print(puan_ozeti([]))Dikkat edin: return ortalama, gecen_sayisi satırı (ortalama, gecen_sayisi) demetini döndürür. Çağıran taraf ortalama, gecen = puan_ozeti(puanlar) yazarak iki değeri doğrudan ayrı değişkenlere alabilir.
Tek sayfa özet
Bu hafta birden fazla değeri birlikte tutmaya başladık.
- Liste sıralı ve değiştirilebilir bir koleksiyondur.
- String dilimleme söz dizimi listelerde de çalışır. Dilimin sonucu yeni bir listedir.
- Liste elemanlarına indeksle erişilebilir ve elemanlar değiştirilebilir. Sınırın dışındaki indeks
IndexErrorverir. append()listenin sonuna eleman ekler,remove()ilk eşleşen değeri siler,indeğerin listede olup olmadığını söyler.append()veremove()Nonedöndürür.- Üzerinde dolaşılan listeyi aynı anda
remove()ile değiştirmek eleman atlatır:[45, 40, 70]listesinden60altını silerken40kalır. Tutulacak değerleri yeni bir listeye eklemek güvenlidir. min()vemax()en küçük ve en büyük değeri doğrudan verir. Döngüyle yazılan “şimdiye kadarki en büyük” kalıbı ise değerin yanında kime ait olduğunu da bulmak gerektiğinde işe yarar.sort()listeyi yerinde değiştirir veNonedöndürür.sorted()asıl listeye dokunmaz, yeni sıralı liste döndürür.forile liste elemanlarında doğrudan dolaşılabilir.- Koşulu sağlayan elemanları yeni bir listeye eklemek filtreleme kalıbıdır.
- Demet sıralıdır ancak değiştirilemez.
- Fonksiyonda
return a, biki değeri bir demet olarak döndürür ve çağıran taraftax, y = ...ile açılabilir. b = aataması bağımsız kopya oluşturmaz, iki değişken aynı listeyi gösterir (aliasing). Bu, kâğıdın fotokopisini değil kendisini vermek gibidir..copy()ya da tam dilim, tek seviyeli listelerde bağımsız bir sığ kopya oluşturur.- Fonksiyona verilen liste kopyalanmaz. Fonksiyon listeyi değiştirirse çağıran koddaki liste de değişir.
- Boş liste ortalama ve maksimum hesabında bir uç durumdur: bölme ve
max()hata verir. Fonksiyon bu durumda ne döndüreceğini açıkça söylemelidir.
Bir sonraki bölümde verilere indeks yerine anlamlı anahtarlarla erişmek için sözlükleri, tekrarsız değerler için ise kümeleri kullanacağız.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
- İndeks ve dilimle liste elemanlarına erişir.
append(),remove()veinile listeyi günceller ve sorgular.sort()ilesorted()arasındaki farkı açıklar.- Liste ile demeti ayırt eder ve fonksiyondan dönen demeti açar.
b = aile aynı nesnenin paylaşıldığını tanır ve bağımsız kopya oluşturur.- Üzerinde dolaşılan listeyi aynı anda değiştirmenin riskini açıklar.
- Boş listeyi ortalama ve maksimum hesabında uç durum olarak denetler.