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 hafta neleri yapabilmelisiniz?

Bölümün sonunda:

  • Liste oluşturabilmeli ve elemanlarına indeks ile erişebilmeli,
  • Liste dilimleme sözdizimini string dilimlemeyle ilişkilendirebilmeli,
  • Listedeki bir elemanı değiştirebilmeli,
  • append() ve remove() ile temel ekleme/silme işlemleri yapabilmeli,
  • in ile üyelik kontrolü yapabilmeli,
  • sort() ile listeyi yerinde, sorted() ile yeni bir sıralı liste üreterek sıralayabilmeli,
  • min() ve max() ile küçük/büyük değeri bulabilmeli,
  • Döngüyle minimum/maksimum arama kalıbını yazabilmeli,
  • len() ile eleman sayısını bulabilmeli,
  • for ile liste elemanları üzerinde gezinebilmeli,
  • Basit toplam, ortalama ve filtreleme işlemleri yapabilmeli,
  • Liste ile demet arasındaki temel farkı ve tek elemanlı demet sözdizimini açıklayabilmeli,
  • Fonksiyondan birden fazla değerin demet olarak döndürülebileceğini ve açılabileceğini açıklayabilmeli,
  • Değiştirilebilir ve değiştirilemez koleksiyon fikrini ayırt edebilmeli,
  • İki değişkenin aynı liste nesnesini göstermesinin etkisini açıklayabilmeli,
  • Bağımsız bir liste kopyası gerektiğinde .copy() veya eşdeğer tam dilim kopyasını tanıyabilmelisiniz.

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.

Important

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)
Note

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.

append ve remove işlemlerinin bir Python listesini nasıl değiştirdiği.

Listeye ekleme ve listeden silme

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.

WarningÜzerinde dolaştığınız listeyi aynı anda silerek değiştirmeyin

Ş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.

Warning

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))  # 92

Ancak 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ığı:

  1. İlk değeri “şimdiye kadarki en yüksek” kabul et.
  2. Kalan her değer için karşılaştır.
  3. Daha yüksek değer bulursan en_yuksek değerini güncelle.
Important

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))  # tuple

Tek 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.

b = a ile aynı listeye referans verme ve a.copy() ile bağımsız liste oluşturma farkı.

Aynı listeyi paylaşma ve kopyalama

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 adet
print(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.

Tip

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ı,
  • 20 ve ü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.

Bölüm özeti

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() ve max() 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, b iki değeri bir demet olarak döndürür ve çağıran tarafta x, y = ... ile açılabilir.
  • b = a ifadesi 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.

Kendinizi kontrol edin

  1. [10, 20, 30][1] sonucu nedir?
  2. [10, 20, 30, 40][1:3] ne üretir?
  3. append() ile remove() ne yapar?
  4. "kalem" in urunler hangi türde sonuç üretir?
  5. sort() ile sorted() arasındaki temel fark nedir?
  6. max([4, 9, 2]) ne döndürür? Döngüyle maksimum ararken neden ilk elemanı başlangıç değeri seçmek kullanışlıdır?
  7. Liste ile demet arasındaki temel fark nedir? (5) ile (5,) neden aynı değildir?
  8. return a, b fonksiyondan hangi türde değer döndürür ve çağıran tarafta nasıl açılabilir?
  9. Neden string’de metin[0] = "A" geçersizken listede liste[0] = "A" geçerlidir?
  10. b = a sonrasında b.append(5) neden a üzerinden de görülebilir?
  11. .copy() veya liste[:] ne zaman kullanılır?
  12. Üzerinde for ile dolaştığınız listeden aynı anda remove() yapmak neden risklidir?
  13. Boş liste ortalama veya maksimum hesabında neden özel durum olabilir?

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.

Back to top