Tek değişken yetmeyince
Anadolu Market'e bugün 5 sipariş geldi. Tutarları t1, t2, t3... diye ayrı değişkenlerde tutmak hem yorucu hem de yarın 500 sipariş gelince imkânsız. Liste (list), birden fazla değeri sıralı biçimde tek değişkende tutar. Köşeli parantezle yazılır, elemanlar virgülle ayrılır ve her türden değer içerebilir.
tutarlar = [1499.9, 250, 89.5, 3200, 475] sehirler = ["İzmir", "Ankara", "İzmir", "Bursa", "İstanbul"] karisik = ["AM-00417", 1499.9, True, None] print(tutarlar) print(len(tutarlar), "sipariş") print(sum(tutarlar), max(tutarlar), min(tutarlar)) print(round(sum(tutarlar) / len(tutarlar), 2), "TL ortalama")
len, sum, max, min hazır fonksiyonları listelerle doğrudan çalışır. Beş satırda bir günün özet raporu çıktı; analistin ilk Python kazancı budur.
İndeks ve dilim
Metinlerdeki kuralların aynısı: 0'dan başlar, negatif sondan sayar, dilimde bitiş hariçtir.
sehirler = ["İzmir", "Ankara", "İzmir", "Bursa", "İstanbul"]
print(sehirler[0], sehirler[-1])
print(sehirler[1:3])
print(sehirler[:2], sehirler[3:])
print("Bursa" in sehirler)
print(sehirler.index("Bursa"))
print(sehirler.count("İzmir"))
in elemanın var olup olmadığını, index ilk konumunu, count kaç kez geçtiğini verir. Var olmayan bir indeks IndexError, var olmayan bir değer için index ValueError verir; önce in ile sormak güvenlidir.
Listeyi değiştirmek
Metinlerden en büyük farkı burada: liste değiştirilebilir (mutable). Metotların çoğu listeyi yerinde değiştirir ve hiçbir şey döndürmez (None).
bekleyen = ["AM-101", "AM-102", "AM-103"]
bekleyen.append("AM-104") # sona ekle
bekleyen.insert(0, "AM-100") # başa ekle (konum, değer)
print(bekleyen)
islenen = bekleyen.pop(0) # konumdaki elemanı çıkar ve döndür
print("İşlendi:", islenen, "| kalan:", bekleyen)
bekleyen.remove("AM-102") # değere göre ilk eşleşeni sil
print(bekleyen)
bekleyen.extend(["AM-105", "AM-106"]) # başka listeyi ekle
print(bekleyen, len(bekleyen))
bekleyen[0] = "AM-101-DUZELTME" # yerinde değiştir
print(bekleyen[0])
| Metot | Ne yapar | Döndürür |
|---|---|---|
append(x) | Sona tek eleman ekler | None |
insert(i, x) | i konumuna ekler | None |
extend(liste) | Başka listenin elemanlarını ekler | None |
pop(i) | i konumundakini çıkarır (varsayılan: son) | Çıkarılan eleman |
remove(x) | x değerinin ilkini siler | None |
sort() | Yerinde sıralar | None |
reverse() | Yerinde ters çevirir | None |
a.append([1, 2]) listenin içine bir liste koyar (iç içe). İki listeyi birleştirmek için extend ya da a + b kullan.sort ve sorted
En sık karıştırılan ikili. liste.sort() listeyi yerinde sıralar ve None döndürür. sorted(liste) ise orijinale dokunmaz, sıralı yeni bir liste döndürür.
tutarlar = [1499.9, 250, 89.5, 3200, 475]
sirali = sorted(tutarlar)
print(sirali)
print(tutarlar) # orijinal aynı
tutarlar.sort(reverse=True) # yerinde, büyükten küçüğe
print(tutarlar)
print("En büyük 3:", tutarlar[:3])
sehirler = ["İzmir", "ankara", "Bursa"]
print(sorted(sehirler))
print(sorted(sehirler, key=str.lower))
Son iki satır önemli bir ayrıntıyı gösterir: sıralama büyük-küçük harfe duyarlıdır, büyük harfler önce gelir. key=str.lower ile "küçük harfli hâline göre sırala" dersin. Türkçe karakterler için tam alfabetik sıralama locale modülü gerektirir; raporda genellikle key=str.lower yeterlidir.
sorted, olmayacaksa sort (bellek kullanmaz). Zincirleme bir ifadede, örneğin sorted(x)[:3], yalnızca sorted çalışır.İç içe liste: küçük bir tablo
Her sipariş birden fazla bilgi taşıyorsa her satır bir liste, tüm satırlar da bir liste olur. Bu, bir tablonun en basit hâlidir.
siparisler = [
["AM-101", "İzmir", 1499.9],
["AM-102", "Ankara", 250],
["AM-103", "İzmir", 89.5],
]
print(siparisler[1]) # ikinci satır
print(siparisler[1][2]) # ikinci satırın tutarı
siparisler[2][2] = 99.5 # tutarı düzelt
print(siparisler[2])
siparisler.append(["AM-104", "Bursa", 3200])
print(len(siparisler), "satır")
print(siparisler[0][1], siparisler[-1][1])
İlk indeks satırı, ikinci indeks sütunu seçer. Döngüleri öğrenince bu yapıyı satır satır gezip şehir bazlı toplam çıkaracaksın; sözlükler dersinde ise sütunlara isimle erişmenin daha okunur yolunu göreceksin.
Listeyi listeyle birleştirmek ve karşılaştırmak
sabah = ["AM-101", "AM-102"] ogle = ["AM-103"] gun = sabah + ogle # yeni liste print(gun) print(sabah * 2) # tekrar print(sabah == ["AM-101", "AM-102"]) # içerik karşılaştırma print([] == [], len([])) print(gun[1:] == ["AM-102", "AM-103"])
+ iki listeden yeni bir liste üretir; extend ise var olanı yerinde büyütür. == sıra ve içerik aynıysa True verir. Boş liste [], koşullarda False gibi davranır; "sipariş var mı" sorusunu if siparisler: ile soracaksın.
Kopyalama tuzağı
Burası herkesin bir kere başına gelen hata. b = a yazmak listeyi kopyalamaz; aynı listeye ikinci bir etiket takar. Birini değiştirdiğinde diğeri de değişir çünkü ikisi de aynı nesnedir.
onayli = ["AM-101", "AM-102"]
yedek = onayli # kopya DEĞİL, aynı liste
yedek.append("AM-103")
print("onayli:", onayli) # o da değişti!
print(onayli is yedek)
gercek_yedek = onayli.copy() # ya da onayli[:] ya da list(onayli)
gercek_yedek.append("AM-999")
print("onayli:", onayli)
print("gercek_yedek:", gercek_yedek)
print(onayli is gercek_yedek)
is operatörü "aynı nesne mi" diye sorar; == ise "içerikleri eşit mi". copy(), [:] ve list() üçü de yüzeysel kopya (shallow copy) alır: dış liste yeni, ama içindeki listeler hâlâ paylaşılır. İç içe listelerde tam bağımsız kopya için copy.deepcopy gerekir.
Demet ve küme Demetler ve kümeler, iç içe veri yapıları İç içe veri, tek satırda liste üretme ise Orta seviyedeki Kapsamlı ifadeler dersinde. Bu bölüm listenin kendisini derinleştiriyor: dilimle değiştirme, yıldızlı açma, anahtarla sıralama, en sık tuzaklar ve hangi işlemin ne kadar pahalı olduğu.
Dilimle okuma, dilimle yazma
Dilim yalnızca okumak için değildir; bir dilime atama yaparak listenin bir bölümünü tek seferde değiştirebilir, silebilir ya da araya eleman ekleyebilirsin. Üçüncü parametre olan adım da listelerde metinlerdeki gibi çalışır.
kuyruk = ["AM-101", "AM-102", "AM-103", "AM-104", "AM-105", "AM-106"] print(kuyruk[::2]) # ikide bir: tek sıradakiler print(kuyruk[::-1]) # ters sıra (yeni liste) print(kuyruk[-3:]) # son üç kuyruk[1:3] = ["AM-102-D"] # iki elemanı bir elemanla değiştir print(kuyruk) kuyruk[1:1] = ["ACIL-1", "ACIL-2"] # araya ekle (hiçbir şey silmeden) print(kuyruk) del kuyruk[-2:] # son ikisini sil print(kuyruk, len(kuyruk)) kuyruk.clear() # tamamını boşalt (aynı liste nesnesi) print(kuyruk)
Dilime atamada sağ tarafın uzunluğu dilimle aynı olmak zorunda değildir; liste kendiliğinden büyür ya da küçülür. del liste[i] tek elemanı, del liste[a:b] bir aralığı siler. clear() ile liste = [] arasındaki fark ise kopyalama tuzağıyla ilgilidir: clear aynı nesneyi boşaltır, dolayısıyla o listeyi gösteren diğer adlar da boş listeyi görür; = [] ise yalnızca bu adı yeni bir listeye taşır.
Liste oluşturmanın yolları ve yıldızlı açma
sira_no = list(range(1, 6)) # [1, 2, 3, 4, 5]
harfler = list("ABC") # her karakter bir eleman
sehirler = "İzmir;Ankara;Bursa".split(";")
bos = [0] * 4
print(sira_no, harfler, sehirler, bos)
ilk, *orta, son = [120, 340, 95, 410, 60]
print(ilk, orta, son)
basliklar, *veri = ["ad,tutar", "Ayşe,120", "Ali,95"]
print(basliklar, "|", veri)
print(*sehirler, sep=" > ") # listeyi argümanlara açYıldızlı ad (*orta) "geri kalan her şeyi bir listede topla" demektir; kalan yoksa boş liste olur. CSV'de başlık satırını veriden ayırmak gibi işlerde çok okunur bir kalıptır. Fonksiyon çağrısında *liste ise ters yönde çalışır ve elemanları ayrı ayrı argüman olarak verir.
Anahtarla sıralama: key
sort, sorted, min ve max bir key fonksiyonu alabilir. Python her elemanı bu fonksiyondan geçirir ve karşılaştırmayı sonuca göre yapar; elemanların kendisi değişmez. Henüz kendi fonksiyonunu yazmadan da hazır fonksiyonlarla çok şey yaparsın:
urunler = ["Çay", "Kahve Makinesi", "Su", "Filtre Kahve"] print(sorted(urunler, key=len)) # kısadan uzuna print(max(urunler, key=len), min(urunler, key=len)) farklar = [-120, 45, -15, 300, -310] print(sorted(farklar, key=abs)) # mutlak değere göre print(max(farklar, key=abs)) # en büyük sapma etiketler = ["b-ürün", "A-ürün", "c-ürün"] print(sorted(etiketler), sorted(etiketler, key=str.lower)) print(list(reversed(urunler))) # ters sırada yeni liste
Python'ın sıralaması kararlıdır (stable): anahtarları eşit olan elemanlar, orijinal sıralarını korur. Yukarıda "Çay" ve "Su" ikisi de kısa ama "Su" daha kısa olduğu için önce gelir; uzunluğu eşit olsaydı listedeki sırayla dizilirlerdi. Birden çok ölçüte göre sıralamayı (önce şehir, sonra tutar) fonksiyonlar dersinden sonra lambda ve demet anahtarlarla yapacaksın.
Çarpmayla iç içe liste tuzağı
[0] * 4 güvenlidir çünkü 0 değiştirilemez. Ama [[0] * 3] * 2 yazdığında dış liste aynı iç listeyi iki kez içerir; birini değiştirince hepsi değişir. Tablo kurarken her satır için yeni bir liste oluşturman gerekir:
yanlis = [[0] * 3] * 2 yanlis[0][0] = 99 print(yanlis) # iki satır birden değişti print(yanlis[0] is yanlis[1]) dogru = [[0] * 3 for _ in range(2)] # her satır ayrı liste dogru[0][0] = 99 print(dogru) print(dogru[0] is dogru[1])
İkinci yazımdaki for _ in range(2) kısmı bir liste kavrayışıdır (list comprehension); döngüleri öğrendikten sonra tam olarak ne yaptığını anlayacaksın. Şimdilik kalıp olarak bil: "her satır için yeni [0] * 3 üret".
Hangi işlem ne kadar pahalı?
Liste, elemanları bellekte art arda tutan bir dizidir. Bu yüzden sondaki işlemler ucuz, baştaki ve ortadaki işlemler pahalıdır: başa eleman eklemek, diğer bütün elemanları bir kaydırmayı gerektirir. Algoritmalar dersinde bunu büyük O gösterimiyle (Big O) tartışacağız; şimdilik tabloyu tanı:
| İşlem | Maliyet | Açıklama |
|---|---|---|
liste[i], liste[i] = x | Sabit, O(1) | Listenin boyundan bağımsız |
append(x), pop() | Sabit, O(1) | Sondan ekle/çıkar |
len(liste) | Sabit, O(1) | Uzunluk saklanır, sayılmaz |
insert(0, x), pop(0) | Doğrusal, O(n) | Tüm elemanlar kayar |
x in liste, index, count, remove | Doğrusal, O(n) | Baştan sona tarama |
liste[a:b], copy() | Dilim boyu kadar | Yeni liste oluşturur |
sort(), sorted() | O(n log n) | Çok verimli Timsort algoritması |
in ile arama yapan bir döngü saniyeler sürer, aynı iş kümeyle (set) anında biter. Kuyruk gibi iki uçtan çalışan yapılar için collections.deque vardır. Önce doğru çalışan kodu yaz, yavaşlık görürsen bu tabloya dön.kayitlar[(sayfa - 1) * boyut : sayfa * boyut]. Dilim sınırları aşınca hata yerine boş liste döndüğü için son sayfanın ötesine geçen istekler de kendiliğinden güvenli olur. Veritabanında aynı iş LIMIT ve OFFSET ile, pandas'ta df.iloc[a:b] ile yapılır; mantık birebir aynıdır.Fonksiyona liste vermek
Bir fonksiyon parametre olarak liste alıp append yaparsa çağıranın listesi de değişir; bu kopyalama tuzağının fonksiyon hâlidir. İyi uygulama: fonksiyon ya listeyi değiştirdiğini adıyla belli eder (listeye_ekle) ya da hiç dokunmayıp yeni liste döndürür. Varsayılan parametre olarak def f(x=[]) yazmak ise klasik bir tuzaktır; fonksiyonlar dersinde göreceksin.
Liste ne zaman yanlış araçtır?
Liste "var mı" sorusunu baştan sona tarayarak yanıtlar; yüz bin müşteri numarasının içinde arama yapacaksan küme (set) ya da sözlük (dict) çok daha hızlıdır. Listenin başından sürekli eleman çıkarmak (pop(0)) da yavaştır; kuyruk için collections.deque vardır. Sayısal hesaplar içinse numpy dizileri hem hızlı hem bellek dostudur. Liste, sıralı ve küçük-orta boyutlu veri için doğru araçtır.
Comprehension'a hazırlık
Orta seviyede [t * 1.2 for t in tutarlar] gibi tek satırda yeni liste üreten liste kavrayışlarını (list comprehension) öğreneceksin. Şimdi append ile adım adım yaptığın her şey orada tek ifadeye inecek; temel sağlamsa geçiş bir gün sürer.