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ı veya bir sınıftaki isimler 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 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; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
b = a yazdım; neden ikisi birden değişti?
Bu bölümde en çok baş ağrıtan hata bu. Bir listeyi ikinci bir ada bağlamak kopya oluşturmaz. İki ad bellekteki aynı listeyi gösterir; birinde yaptığınız değişikliği ötekinde de görürsünüz.
Liste oluşturmak
Bir liste köşeli parantezlerle oluşturulur:
puanlar = [70, 85, 92, 60]Liste değerleri belirli bir sırayla tutar ve her değerin bir konumu (indeksi) vardır.
Değer: 70 85 92 60
İndeks: 0 1 2 3
String’lerde olduğu gibi indeksler 0’dan başlar.
puanlar = [70, 85, 92, 60]
print(puanlar[0])
print(puanlar[2])
print(puanlar[-1])Çıktı:
70
92
60
Liste dilimleme
Geçen hafta string’lerde kullandığımız dilimleme söz dizimi listelerde de çalışır:
puanlar = [70, 85, 92, 60, 78]
print(puanlar[1:4])
print(puanlar[:3])
print(puanlar[-2:])Çıktı:
[85, 92, 60]
[70, 85, 92]
[60, 78]
String dilimlemede olduğu gibi başlangıç dâhil, bitiş hariçtir. Ancak önemli bir fark vardır: sonuç artık string değil, yeni bir liste olur.
Tam dilim ([:], başı ve sonu boş bırakılmış dilim) listenin kopyasını verir:
orijinal = [1, 2, 3]
kopya = orijinal[:]
kopya.append(4)
print(orijinal) # [1, 2, 3]
print(kopya) # [1, 2, 3, 4]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 elemanlar kopyalanmaz. İçinde başka liste olmayan (tek seviyeli) bir listede bu ayrıntı sorun çıkarmaz.
Liste elemanını değiştirmek
Geçen hafta 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.
puanlar = [70, 85, 92]
puanlar[0] = 75
print(puanlar)Sonuç:
[75, 85, 92]
Burada yeni bir liste oluşturulmadı; mevcut listenin ilk elemanı değiştirildi.
Liste indeksi de listenin sınırları içinde kalmalıdır. Üç elemanlı bir listede geçerli pozitif indeksler 0, 1 ve 2’dir.
append(), remove() ve in
Listenin sonuna yeni eleman eklemek için append() kullanılır:
urunler = ["defter", "kalem"]
urunler.append("silgi")
print(urunler)Çıktı:
['defter', 'kalem', 'silgi']
Belirli bir değeri listeden kaldırmak için remove() kullanılabilir:
urunler.remove("kalem")
print(urunler)Çıktı:
['defter', 'silgi']
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ığını kontrol etmek isteyebilirsiniz:
if "kalem" in urunler:
urunler.remove("kalem")in, bir değerin listede olup olmadığını denetler ve True ya da False verir:
print("silgi" in urunler)
print("cetvel" in urunler)append() ve remove() yeni bir liste döndürmez, var olan listeyi değiştirir. Bu yüzden liste = liste.append(x) yazmayın.
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:
puanlar = [70, 85, 92]
for puan in puanlar:
print(puan)Burada puan, her yinelemede listenin sıradaki değerini alır.
Bir toplam hesabı:
puanlar = [70, 85, 92]
toplam = 0
for puan in puanlar:
toplam = toplam + puan
print(toplam)Listede gezinmek için çoğu zaman range() gerekmez. Yalnızca elemanlar lazımsa for ile doğrudan listede dolaşmak daha okunur.
Şöyle bir kod bazı elemanları atlayabilir:
sayilar = [1, 2, 2, 3]
for sayi in sayilar:
if sayi == 2:
sayilar.remove(sayi)Döngü sürerken listenin uzunluğu ve elemanların konumu değişir. Bu yüzden bazı elemanlar hiç işlenmeden atlanabilir. Daha güvenli yol, tutmak istediğiniz değerleri yeni bir listeye eklemek ya da gerçekten gerekiyorsa listenin kopyasında dolaşmaktır.
Ortalama hesaplamak
Bir listenin ortalaması için önce toplamı, sonra eleman sayısını kullanabiliriz:
puanlar = [70, 85, 92, 73]
toplam = 0
for puan in puanlar:
toplam = toplam + puan
ortalama = toplam / len(puanlar)
print(ortalama)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 nasıl çalıştığını görürsünüz.
Boş listenin eleman sayısı 0’dır. toplam / len(puanlar) ifadesi boş listede sıfıra bölme hatası verir. 7. haftada gördüğümüz gibi bu bir uç durumdur.
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:
puanlar = [70, 85, 92, 60]
print(min(puanlar)) # 60
print(max(puanlar)) # 92Yine 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:
puanlar = [70, 85, 92, 60]
en_yuksek = puanlar[0]
for puan in puanlar[1:]:
if puan > en_yuksek:
en_yuksek = puan
print(en_yuksek)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] ve max(puanlar) hata verir. Fonksiyonu yazarken “liste boş gelirse ne döndürecek?” sorusunu da cevaplamalı ve bunu fonksiyonun sözleşmesine yazmalısınız. 11. haftada kayıtlarla çalışırken bu durumu None döndürerek ele alacağız.
Sıralama: sort() ve sorted()
Bir listeyi küçükten büyüğe sıralamanın iki yolu vardır:
puanlar = [70, 92, 60, 85]
puanlar.sort()
print(puanlar)sort() mevcut listeyi değiştirir.
Orijinal sırayı korumak istiyorsanız sorted() kullanın; bu fonksiyon yeni bir liste döndürür:
puanlar = [70, 92, 60, 85]
sirali = sorted(puanlar)
print(puanlar) # [70, 92, 60, 85]
print(sirali) # [60, 70, 85, 92]Ters sıralama için:
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:
sicakliklar = [12, 25, 31, 18, 35]
sicak_gunler = []
for sicaklik in sicakliklar:
if sicaklik >= 30:
sicak_gunler.append(sicaklik)
print(sicak_gunler)Sonuç:
[31, 35]
Buna filtreleme denir: yalnızca koşulu sağlayan elemanlar yeni listeye eklenir.
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:
koordinat = (41.2867, 36.33)
print(koordinat[0])
print(koordinat[1])Bir demetin elemanına yeniden değer atayamayız:
koordinat = (41.2867, 36.33)
# koordinat[0] = 42 # Geçerli değildir.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:
boyut = (1920, 1080)
genislik, yukseklik = boyut
print(genislik)
print(yukseklik)Buna demet açma (tuple unpacking) denir.
return satırında değerleri virgülle ayırırsak Python bunları tek bir demette toplar:
def sinirlar():
return 0, 100
sonuc = sinirlar()
print(sonuc) # (0, 100)
print(type(sonuc)) # tupleŞu iki return aynı demeti döndürür:
return 0, 100
return (0, 100)Çağıran tarafta demeti doğrudan açabiliriz:
alt, ust = sinirlar()
print(alt)
print(ust)Aynı listeye referans verme
Listeler değiştirilebilir olduğu için şu davranışa dikkat etmek gerekir:
liste1 = [10, 20, 30]
liste2 = liste1
liste2.append(40)
print(liste1)
print(liste2)İki çıktı da:
[10, 20, 30, 40]
olur.
Çünkü 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.
Bunu şöyle düşünebilirsiniz:
liste1 ─┐
├──> [10, 20, 30]
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:
orijinal = [1, 2, 3]
kopya = orijinal.copy()
kopya.append(4)
print(orijinal)
print(kopya)Çıktı:
[1, 2, 3]
[1, 2, 3, 4]
Daha önce gördüğümüz orijinal[:] de aynı sonucu verir. İç içe listelerin kopyalanmasına bu derste girmiyoruz; .copy() ile tam dilimin sığ kopya oluşturduğunu bilmeniz yeterli.
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:
def en_az_gecen_sayisi(puanlar):
adet = 0
for puan in puanlar:
if puan >= 60:
adet = adet + 1
return adetprint(en_az_gecen_sayisi([45, 70, 81, 50]))Sonuç 2’dir.
Fonksiyon listeyi yalnızca okuyorsa ne yaptığını izlemek kolaydır. append() ya da indeksle atama yaparak listeyi değiştiriyorsa çağıran koddaki liste de değişir, çünkü fonksiyon listenin kopyasıyla değil kendisiyle çalışır.
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.
Üret: ölçüm özetini hesaplayın
Bir sensörün sıcaklık ölçümleri şu listede olsun:
olcumler = [18.5, 21.0, 19.5, 24.0, 22.5]olcum_ozeti(olcumler) adlı bir fonksiyon yazın. Fonksiyon:
- Toplamı döngüyle hesaplamalı,
- Ortalamayı bulmalı,
20ve üzeri ölçümlerin sayısını hesaplamalı,- Sonucu
(ortalama, sicak_olcum_sayisi)biçiminde bir demet olarak döndürmeli.
Başlangıç:
Beklenen yaklaşık sonuç:
(21.1, 3)
Toplam ve 20 üzeri sayaç için iki ayrı biriktirici kullanabilirsiniz. Ortalama toplam / len(olcumler) olur.
def olcum_ozeti(olcumler):
toplam = 0
sicak_olcum_sayisi = 0
for olcum in olcumler:
toplam = toplam + olcum
if olcum >= 20:
sicak_olcum_sayisi = sicak_olcum_sayisi + 1
ortalama = toplam / len(olcumler)
return ortalama, sicak_olcum_sayisi
ortalama, sicak_sayisi = olcum_ozeti([18.5, 21.0, 19.5, 24.0, 22.5])
print(ortalama)
print(sicak_sayisi)Dikkat edin: return ortalama, sicak_olcum_sayisi satırı (ortalama, sicak_olcum_sayisi) demetini döndürür. Çağıran taraf ortalama, sicak_sayisi = ... yazarak iki değeri doğrudan ayrı değişkenlere alır.
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; dilim yeni bir liste verir ve
liste[:]sığ kopya oluşturur. - Liste elemanlarına indeksle erişilebilir ve elemanlar değiştirilebilir.
append()listenin sonuna eleman ekler;remove()ilk eşleşen değeri siler;indeğerin listede olup olmadığını söyler.- Üzerinde dolaşılan listeyi aynı anda
remove()ile değiştirmek eleman atlamalarına neden olabilir. 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;sorted()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 = abağımsız kopya oluşturmaz; iki değişken aynı listeyi gösterir..copy()ya da tam dilim, tek seviyeli listelerde bağımsız bir sığ kopya oluşturur.- Fonksiyonun kendisine verilen listeyi değiştirip değiştirmeyeceği bilerek verilmesi gereken bir tasarım kararıdır.
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.