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 en temel koleksiyonlarından olan liste (list) ve demet (tuple) yapılarını öğreneceğiz. Ayrıca listelerin değiştirilebilir olması nedeniyle iki değişkenin aynı listeyi göstermesi durumunda ortaya çıkan davranışı başlangıç düzeyinde inceleyeceğiz.
Bu bölümün kapsamı
- indeks ve dilimle liste elemanlarına erişim
append(),remove()veindavranışlarısort()ilesorted()farkı- liste ile demet ayrımı ve tek elemanlı demet yazımı
- aynı nesnenin paylaşılması ve bağımsız kopya üretme
- üzerinde dolaşılan listeyi aynı anda değiştirmenin riski
- boş listenin ortalama ve maksimum hesabında uç durum olması
- liste kavraması (list comprehension)
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 üretmez; iki ad aynı nesneyi gösterir ve birinden yapılan değişiklik diğerinden de görünür.
Liste oluşturmak
Bir liste köşeli parantezlerle oluşturulur:
puanlar = [70, 85, 92, 60]Liste birden fazla değeri sıralı biçimde tutar.
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özdizimi 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ıç dahil, bitiş hariçtir. Ancak önemli bir fark vardır: sonuç artık string değil, yeni bir liste olur.
Tam dilim de sığ bir liste kopyası üretir:
orijinal = [1, 2, 3]
kopya = orijinal[:]
kopya.append(4)
print(orijinal) # [1, 2, 3]
print(kopya) # [1, 2, 3, 4]Bu derste bağımsız kopya niyetini daha açık anlattığı için çoğunlukla .copy() kullanacağız; fakat başka kaynaklarda liste[:] gördüğünüzde bunun tek seviyeli listede benzer bir sığ kopya kalıbı olduğunu tanıyın.
Liste elemanını değiştirmek
String değerlerin değiştirilemez olduğunu geçen hafta gördük. Listeler ise değiştirilebilir (mutable) yapılardır.
puanlar = [70, 85, 92]
puanlar[0] = 75
print(puanlar)Sonuç:
[75, 85, 92]
Burada yeni bir liste oluşturmak yerine mevcut listenin ilk elemanı değiştirildi.
Liste indeksleri de sınırların içinde olmalıdır. Üç elemanlı bir listede geçerli pozitif indeksler 0, 1, 2 değerleridir.
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 ValueError oluşur. Bu nedenle varlığı önceden sınamak isteyebilirsiniz:
if "kalem" in urunler:
urunler.remove("kalem")in üyelik kontrolü True veya False döndürür:
print("silgi" in urunler)
print("cetvel" in urunler)append() ve remove() mevcut listeyi değiştirir. Yani bu metotların dönüş değerini yeni liste sanıp liste = liste.append(x) biçiminde 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)Liste üzerinde gezinirken çoğu zaman ayrıca range() kullanmaya gerek yoktur. Eğer yalnızca elemanlara ihtiyacımız varsa doğrudan liste üzerinde for daha okunurdur.
Şu tür kodlar beklenmeyen eleman atlamalarına yol açabilir:
sayilar = [1, 2, 2, 3]
for sayi in sayilar:
if sayi == 2:
sayilar.remove(sayi)Döngü devam ederken listenin uzunluğu ve elemanların konumu değiştiği için sıradaki bazı öğeler işlenmeden geçilebilir. Başlangıç düzeyinde daha güvenli yaklaşım, filtrelenecek değerleri yeni bir listeye eklemektir veya gerçekten gerekiyorsa kopya üzerinde 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 sum() gibi hazır fonksiyonlar vardır; ancak bu aşamada biriktirici mantığını görünür tutmak için toplamı döngüyle hesaplamak öğreticidir.
Boş listenin eleman sayısı 0dır. toplam / len(puanlar) ifadesi boş listede sıfıra bölme hatası üretir. 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)) # 92Ancak maksimum arama kalıbını döngüyle de bilmek önemlidir; çünkü ileride “puanı en yüksek öğrencinin adı” gibi yalnızca sayıyı değil, o sayıyla ilişkili kaydı 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)İzleme mantığı:
- İlk değeri “şimdiye kadarki en yüksek” kabul et.
- Kalan her değer için karşılaştır.
- Daha yüksek değer bulursan
en_yuksekdeğerini güncelle.
Bu başlangıç kalıbı listenin boş olmadığını varsayar. Boş listede puanlar[0] ve max(puanlar) geçerli değildir. Fonksiyon tasarlarken “boş veri gelirse ne dönecek?” sözleşmesinin ayrıca belirlenmesi gerekir. 11. haftada kayıtlar için bu kararı None ile açıkça ele alacağız.
Sıralama: sort() ve sorted()
Bir listeyi küçükten büyüğe sıralamak için iki temel araç vardır:
puanlar = [70, 92, 60, 85]
puanlar.sort()
print(puanlar)sort() mevcut listeyi değiştirir.
Orijinal sırayı korumak istiyorsanız sorted() 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))Bu derste sıralama algoritmalarının nasıl çalıştığını ayrıntılı olarak uygulamayacağız; onları Veri Yapıları ve Programlama dersinde derinleştireceğiz. Buradaki hedef, hazır listenin sırasını değiştirmek ile yeni sıralı liste üretmek arasındaki farkı bilmektir.
Alıştırma — geçen puanları sayın
Aşağıdaki listedeki 60 ve üzeri puanların kaç tane olduğunu bulun.
Beklenen sonuç 3tü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]
Bu, temel bir filtreleme kalıbıdır: yalnızca koşulu sağlayan elemanları yeni koleksiyona ekleriz.
Demet (tuple)
Demet, listeye benzeyen sıralı bir koleksiyondur; ancak elemanları sonradan değiştirilemez.
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 nedenle:
- Liste: içerik zamanla değişecekse uygundur,
- Demet: küçük ve sabit bir değer grubu olarak düşünülüyorsa uygundur.
Örneğin RGB renk bileşenleri veya bir koordinat çifti demetle anlamlı olabilir.
Tek elemanlı demet tuzağı
Parantez tek başına demet oluşturmaz:
x = (5)
y = (5,)
print(type(x)) # int
print(type(y)) # tupleTek elemanlı demeti belirleyen şey sondaki virgüldür. (5) yalnızca 5 ifadesini paranteze alır; (5,) ise bir elemanlı demettir.
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)Bu yapıya tuple unpacking (demet açma) denir.
Fonksiyonda virgülle birden fazla değer döndürdüğümüzde Python bunları bir demet olarak paketler:
def sinirlar():
return 0, 100
sonuc = sinirlar()
print(sonuc) # (0, 100)
print(type(sonuc)) # tupleŞu iki return aynı fikri ifade eder:
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
Listelerin değiştirilebilir olması önemli bir davranışa yol açar.
liste1 = [10, 20, 30]
liste2 = liste1
liste2.append(40)
print(liste1)
print(liste2)İki çıktı da:
[10, 20, 30, 40]
olur.
Çünkü liste2 = liste1 ifadesi bağımsız ikinci bir liste oluşturmaz. İki değişken aynı liste nesnesini gösterir. Bu duruma aynı nesneye referans verme (aliasing) denir.
Bunu şöyle düşünebilirsiniz:
liste1 ─┐
├──> [10, 20, 30]
liste2 ─┘
liste2 üzerinden yapılan değişiklik aynı nesne üzerinde gerçekleştiği için liste1 üzerinden de görülür.
Tahmin et — iki değişken mi, iki liste mi?
Çalıştırmadan önce çıktıyı tahmin edin:
Bu örneğin amacı “Python gizlice değerleri değiştiriyor” düşüncesini önlemektir. Değişen tek liste nesnesidir; iki ad da onu göstermektedir.
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 tek seviyeli listede benzer bir sığ kopya üretir. Bu aşamada iç içe listelerde kopyalama ayrıntılarına girmiyoruz; .copy() kullanımını ve tam dilimi sığ (shallow) kopya olarak tanıyoruz.
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
Fonksiyona liste argümanı 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ç 2dir.
Fonksiyon listeyi yalnızca okuyorsa davranışı daha kolay izlenir. Eğer fonksiyon append() veya indeks atamasıyla listeyi değiştiriyorsa çağıran kodun listesi de etkilenebilir.
Bir fonksiyonun kendisine verilen listeyi değiştirip değiştirmediği önemli bir tasarım bilgisidir. Başlangıçta hesaplama fonksiyonlarında listeyi yalnızca okumayı, yeni sonuç gerekiyorsa yeni değer döndürmeyi tercih edin.
Ü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 ifadesi (ortalama, sicak_olcum_sayisi) demeti döndürür. Çaıran tarafta ortalama, sicak_sayisi = ... ile iki değer doğrudan açılabilir.
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özdizimi listelerde de çalışır; dilim yeni liste üretir ve
liste[:]sığ kopya olarak görülebilir. - Liste elemanlarına indeksle erişilebilir ve elemanlar değiştirilebilir.
append()listenin sonuna eleman ekler;remove()ilk eşleşen değeri siler;inüyelik kontrolü yapar.- Üzerinde dolaşılan listeyi aynı anda
remove()ile değiştirmek eleman atlamalarına neden olabilir. min()vemax()hazır küçük/büyük değer araçlarıdır; döngüyle “şimdiye kadarki en büyük” kalıbı ilişkili kaydı bulacağımız problemlere temel oluşturur.sort()listeyi yerinde değiştirir;sorted()yeni sıralı liste döndürür.for, liste elemanlarını doğrudan işlemek için kullanılabilir.- Yeni bir listeye yalnızca koşulu sağlayan elemanları eklemek temel filtreleme kalıbıdır.
- Demet sıralıdır ancak değiştirilemez; tek elemanlı demet
(5,)biçiminde yazılır. - Fonksiyonda
return a, biki değeri bir demet olarak döndürür ve çağıran taraftax, y = ...ile açılabilir. b = aifadesi listelerde bağımsız kopya üretmez; iki değişken aynı nesneyi gösterebilir..copy()veya tam dilim tek seviyeli listelerde bağımsız sığ kopya oluşturabilir.- Fonksiyona verilen değiştirilebilir koleksiyonun değiştirilip değiştirilmediği önemli 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()veindavranışlarını uygular.sort()ilesorted()arasındaki farkı ayırır.- Liste ile demeti ayırır ve tek elemanlı demetin yazımını bilir.
b = aile aynı nesnenin paylaşıldığını tanır ve bağımsız kopya üretir.- Üzerinde dolaşılan listeyi aynı anda değiştirmenin riskini açıklar.
- Boş listeyi ortalama ve maksimum hesabında uç durum olarak ele alır.