Bütünleştirici mikro uygulama
Bu bölümde yeni bir Python kavramı öğrenmeyeceğiz. Amaç, ilk 13 haftada öğrendiğimiz yapıları küçük ama gerçekçi bir problem üzerinde birlikte kullanmak:
- değişkenler ve ifadeler,
- koşullar,
- döngüler,
- fonksiyonlar,
- string işlemleri,
- listeler ve sözlükler,
- doğrulama,
- küçük testler,
- hata ayıklama.
Mikro uygulamamız bir kampüs kırtasiye satış özeti olacak. Hazır verilen küçük bir veri kümesini işleyecek, hatalı kayıtları ayıracak ve özet sonuçlar üreteceğiz.
Bu hafta neleri yapabilmelisiniz?
Bölümün sonunda:
- hazır bir kod tabanını okuyup veri akışını açıklayabilmeli,
- problemi küçük fonksiyonlara ayırabilmeli,
- verilen kabul kriterlerine göre sınırlı özellik ekleyebilmeli,
- liste ve sözlük kayıtlarını filtreleyip özetleyebilmeli,
- string normalleştirme ve sayısal doğrulama yapabilmeli,
assertile küçük davranış kontrolleri yazabilmeli,- hatalı girdinin programın geri kalanını bozmasını önleyebilmeli,
- bir değişiklikten sonra ilgili örnekleri yeniden çalıştırabilmeli,
- yazdığınız kodun ne yaptığını kısa ve teknik olarak doğru biçimde açıklayabilmelisiniz.
Problem
Bir kırtasiye uygulamasında satış kayıtları şu biçimde geliyor:
kayitlar = [
{"urun": " Kalem ", "fiyat": "12.5", "adet": "3"},
{"urun": "Defter", "fiyat": "45", "adet": "2"},
{"urun": "Silgi", "fiyat": "8", "adet": "abc"},
{"urun": "Kalem", "fiyat": "12.5", "adet": "1"},
]Burada iki önemli nokta var:
- Metin alanlarında gereksiz boşluk olabilir.
- Sayısal olması gereken alanlar string olarak gelebilir ve bazıları geçersiz olabilir.
İstenen rapor:
Geçerli kayıt sayısı: 3
Geçersiz kayıt sayısı: 1
Toplam ciro: 140.0
Ürün adetleri: {'kalem': 4, 'defter': 2}
Silgi kaydındaki "abc" adet değeri sayıya dönüştürülemediği için o kayıt geçersiz sayılacaktır.
Önce veri akışını düşünün
Koda başlamadan önce problemi şu adımlara ayıralım:
ham kayıt
↓
metinleri temizle
↓
fiyat ve adedi sayıya dönüştür
↓
kayıt geçerli mi?
↓
geçerliyse ciro ve ürün adetlerine ekle
↓
özet üret
Bu akışın her adımını ayrı fonksiyona ayırmak zorunda değiliz; ancak birbirinden farklı sorumlulukları görünür tutmak kodu kolaylaştırır.

Hazır yardımcı fonksiyon: ürün adını temizlemek
def urun_adi_temizle(urun):
return urun.strip().lower()Beklenen davranış:
assert urun_adi_temizle(" Kalem ") == "kalem"
assert urun_adi_temizle("DEFTER") == "defter"Bu fonksiyon saf bir string dönüşümüdür.
Sayısal alanı güvenli çözmek
- haftadaki istisna bilgisini kullanalım:
def pozitif_sayi_coz(metin):
try:
sayi = float(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayiÖrnek kontroller:
assert pozitif_sayi_coz("12.5") == 12.5
assert pozitif_sayi_coz("0") == 0
assert pozitif_sayi_coz("abc") is None
assert pozitif_sayi_coz("-3") is NoneAdet için tam sayı beklememiz gerektiğinden ayrı bir fonksiyon daha anlamlıdır:
def pozitif_tam_sayi_coz(metin):
try:
sayi = int(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayiBu iki fonksiyon benzer görünse de biri float, diğeri int sözleşmesine sahiptir.
Tahmin et — hangi kayıt geçersiz?
Aşağıdaki küçük kontrolü çalıştırmadan önce sonucu tahmin edin:
Hangi iki değer None üretir? Çalıştırıp doğrulayın.
Bir kaydı dönüştürmek
Ham kayıt:
{"urun": " Kalem ", "fiyat": "12.5", "adet": "3"}Program içinde şu yapıya dönüşebilir:
{"urun": "kalem", "fiyat": 12.5, "adet": 3}Bunu fonksiyonla yapalım:
def kaydi_coz(kayit):
urun = urun_adi_temizle(kayit["urun"])
fiyat = pozitif_sayi_coz(kayit["fiyat"])
adet = pozitif_tam_sayi_coz(kayit["adet"])
if fiyat is None or adet is None:
return None
return {
"urun": urun,
"fiyat": fiyat,
"adet": adet
}None, bu uygulamada “kayıt geçerli biçime dönüştürülemedi” anlamını taşır.
Gerçek bir sistemde geçersiz kaydın neden geçersiz olduğuna dair daha ayrıntılı hata bilgisi tutmak isteyebiliriz. Bu mikro uygulamada kapsamı küçük tutmak için yalnızca None kullanıyoruz.
Alıştırma — kayıt doğrulamasını tamamlayın
Aşağıdaki fonksiyonda eksik olan kontrolü tamamlayın.
Beklenen sonuç:
None
if fiyat is None or adet is None: koşulu yeterlidir.
def pozitif_sayi_coz(metin):
try:
sayi = float(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayi
def pozitif_tam_sayi_coz(metin):
try:
sayi = int(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayi
def kaydi_coz(kayit):
urun = kayit["urun"].strip().lower()
fiyat = pozitif_sayi_coz(kayit["fiyat"])
adet = pozitif_tam_sayi_coz(kayit["adet"])
if fiyat is None or adet is None:
return None
return {
"urun": urun,
"fiyat": fiyat,
"adet": adet
}
print(kaydi_coz({"urun": "Silgi", "fiyat": "8", "adet": "abc"}))Satır toplamı
Geçerli kaydın ciro katkısı:
fiyat × adet
Bunu küçük bir fonksiyonla açık hâle getirebiliriz:
def satir_toplami(kayit):
return kayit["fiyat"] * kayit["adet"]Kontroller:
assert satir_toplami({"fiyat": 12.5, "adet": 3}) == 37.5
assert satir_toplami({"fiyat": 45, "adet": 2}) == 90Ürün bazında adet biriktirmek
Geçerli kayıtların ürün adetlerini sözlükte toplayabiliriz:
urun_adetleri = {}
urun = "kalem"
adet = 3
urun_adetleri[urun] = urun_adetleri.get(urun, 0) + adetAynı ürün yeniden geldiğinde mevcut miktar üzerine eklenir.
Ana rapor fonksiyonu
Şimdi parçaları birleştirelim:
def satis_raporu(kayitlar):
toplam_ciro = 0
gecerli_sayisi = 0
gecersiz_sayisi = 0
urun_adetleri = {}
for ham_kayit in kayitlar:
kayit = kaydi_coz(ham_kayit)
if kayit is None:
gecersiz_sayisi = gecersiz_sayisi + 1
continue
gecerli_sayisi = gecerli_sayisi + 1
toplam_ciro = toplam_ciro + satir_toplami(kayit)
urun = kayit["urun"]
adet = kayit["adet"]
urun_adetleri[urun] = urun_adetleri.get(urun, 0) + adet
return {
"gecerli": gecerli_sayisi,
"gecersiz": gecersiz_sayisi,
"ciro": toplam_ciro,
"urun_adetleri": urun_adetleri
}Bu fonksiyon yeni sözdizimi içermez. Önceki haftalardaki kalıpları bir araya getirir:
forile kayıtları dolaşma,- fonksiyon çağırma,
Nonekontrolü,continue,- biriktirici,
- sözlük sayacı,
- sözlük sonucu döndürme.
Kod izleme: ilk iki kayıt
Tüm programı bir anda anlamak yerine ilk iki kaydı izleyelim.
Başlangıç:
toplam_ciro = 0
gecerli = 0
gecersiz = 0
urun_adetleri = {}
İlk kayıt:
{"urun": " Kalem ", "fiyat": "12.5", "adet": "3"}Dönüştürülmüş:
{"urun": "kalem", "fiyat": 12.5, "adet": 3}Yeni durum:
toplam_ciro = 37.5
gecerli = 1
gecersiz = 0
urun_adetleri = {"kalem": 3}
İkinci kayıt:
{"urun": "Defter", "fiyat": "45", "adet": "2"}Yeni durum:
toplam_ciro = 127.5
gecerli = 2
gecersiz = 0
urun_adetleri = {"kalem": 3, "defter": 2}
Bu tür izleme, büyük hücrede sonuç yanlış olduğunda hangi adımda sapma başladığını bulmaya yardım eder.
Alıştırma — rapor biriktiricilerini tamamlayın
Aşağıdaki sürümde rapor fonksiyonunun üç güncellemesi eksik.
Beklenen sözlük:
{
'gecerli': 3,
'gecersiz': 1,
'ciro': 140.0,
'urun_adetleri': {'kalem': 4, 'defter': 2}
}
Geçerli kayıtta: gecerli_sayisi = gecerli_sayisi + 1, toplam_ciro = toplam_ciro + satir_toplami(kayit) ve ürün için get() kalıbı gerekir.
def urun_adi_temizle(urun):
return urun.strip().lower()
def pozitif_sayi_coz(metin):
try:
sayi = float(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayi
def pozitif_tam_sayi_coz(metin):
try:
sayi = int(metin)
except ValueError:
return None
if sayi < 0:
return None
return sayi
def kaydi_coz(kayit):
urun = urun_adi_temizle(kayit["urun"])
fiyat = pozitif_sayi_coz(kayit["fiyat"])
adet = pozitif_tam_sayi_coz(kayit["adet"])
if fiyat is None or adet is None:
return None
return {"urun": urun, "fiyat": fiyat, "adet": adet}
def satir_toplami(kayit):
return kayit["fiyat"] * kayit["adet"]
def satis_raporu(kayitlar):
toplam_ciro = 0
gecerli_sayisi = 0
gecersiz_sayisi = 0
urun_adetleri = {}
for ham_kayit in kayitlar:
kayit = kaydi_coz(ham_kayit)
if kayit is None:
gecersiz_sayisi = gecersiz_sayisi + 1
continue
gecerli_sayisi = gecerli_sayisi + 1
toplam_ciro = toplam_ciro + satir_toplami(kayit)
urun = kayit["urun"]
adet = kayit["adet"]
urun_adetleri[urun] = urun_adetleri.get(urun, 0) + adet
return {
"gecerli": gecerli_sayisi,
"gecersiz": gecersiz_sayisi,
"ciro": toplam_ciro,
"urun_adetleri": urun_adetleri
}
kayitlar = [
{"urun": " Kalem ", "fiyat": "12.5", "adet": "3"},
{"urun": "Defter", "fiyat": "45", "adet": "2"},
{"urun": "Silgi", "fiyat": "8", "adet": "abc"},
{"urun": "Kalem", "fiyat": "12.5", "adet": "1"},
]
print(satis_raporu(kayitlar))Küçük regresyon kontrolleri
Bir değişiklik yaptıktan sonra daha önce çalışan davranışların bozulmadığını kontrol etmek için birkaç assert yazabiliriz.
assert urun_adi_temizle(" Kalem ") == "kalem"
assert pozitif_sayi_coz("12.5") == 12.5
assert pozitif_sayi_coz("abc") is None
assert pozitif_tam_sayi_coz("3") == 3
assert pozitif_tam_sayi_coz("-1") is NoneRapor için:
test_rapor = satis_raporu([
{"urun": "Kalem", "fiyat": "10", "adet": "2"}
])
assert test_rapor["gecerli"] == 1
assert test_rapor["gecersiz"] == 0
assert test_rapor["ciro"] == 20
assert test_rapor["urun_adetleri"]["kalem"] == 2Bu kontroller tam bir test sistemi değildir; fakat küçük bir değişiklik sonrası temel davranışın korunup korunmadığını hızlıca gösterir.
Hata ayıklama senaryosu
Rapor ciro = 152 gibi beklenmeyen bir sonuç verirse doğrudan bütün fonksiyonları değiştirmeyin.
Şu sırayı izleyin:
- Beklenen sonucu elle hesaplayın.
- Geçerli kayıtların hangileri olduğunu belirleyin.
kaydi_coz()çıktısını her kayıt için inceleyin.- Her geçerli kayıt için
satir_toplami()sonucunu kontrol edin. - Biriktiricinin hangi yinelemede yanlışlaştığını bulun.
- Tek bir düzeltme yapıp aynı veriyle yeniden çalıştırın.
assertkontrollerini yeniden çalıştırın.
Bu süreç 13. haftadaki sistematik hata ayıklama yaklaşımının uygulamasıdır.
Sınırlı özellik ekleme
Hazır uygulamaya şu özelliklerden yalnızca birini eklemeyi deneyin:
- ciroya en fazla katkı yapan ürün adını bulmak,
- stoku olmayan kayıtları ayrı saymak,
- ürün adı boş string ise kaydı geçersiz saymak,
- ciroyu iki ondalık basamakla raporlamak.
Özellik eklerken önce kabul kriterini yazın.
Örneğin:
Ürün adı boş veya yalnızca boşluklardan oluşuyorsa kayıt geçersiz olmalı.
Sonra önce bir başarısız örnek hazırlayın:
bos_ad = {"urun": " ", "fiyat": "10", "adet": "1"}Ancak bundan sonra kodu değiştirin.
Yeni özellik eklemek, çalışan kodu baştan yazmak anlamına gelmez. En küçük gerekli değişikliği yapmak ve ardından mevcut kontrolleri yeniden çalıştırmak daha güvenlidir.
Kodunuzu açıklayın
Bir programın yalnızca çıktısını göstermek yeterli değildir. Kısa bir teknik açıklama da yapabilmelisiniz.
Örnek:
kaydi_cozfonksiyonu ürün adını normalleştirir ve sayısal alanları dönüştürür. Fiyat veya adet dönüştürülemiyorsaNonedöndürür.satis_raporuyalnızca geçerli kayıtları ciro ve ürün adetleri biriktiricilerine ekler; geçersiz kayıtları ayrı sayaçta tutar.
Bu açıklama:
- fonksiyon adlarını kullanır,
- veri akışını belirtir,
- gereksiz satır satır kod tekrarı yapmaz,
- sonucun neden doğru olduğunu anlatır.
Ders sonu kontrolü
Mikro uygulamanız için şu sorulara cevap verebilmelisiniz:
- Ham veri ile temizlenmiş veri arasındaki fark nedir?
- Geçersiz kayıt hangi noktada elenir?
- Neden string sayı alanları dönüştürülür?
Noneburada hangi anlamda kullanılıyor?- Ciro hangi değişkende ve nasıl biriktiriliyor?
- Aynı ürünün farklı kayıtları nasıl tek sözlük anahtarında toplanıyor?
- Fonksiyonların hangileri saf hesaplama/dönüşüm yapıyor?
- Bir sonuç yanlışsa ilk hangi küçük fonksiyonu veya ara değeri kontrol edersiniz?
- Yeni özellik eklendikten sonra hangi mevcut davranışların yeniden doğrulanması gerekir?
Dönem özeti
Bu ders boyunca programlamayı yalnızca Python sözdizimi olarak ele almadık. Başlangıçtan itibaren şu çalışma biçimini geliştirdik:
problemi anla
↓
girdi–işlem–çıktıyı belirle
↓
küçük çözüm adımları oluştur
↓
kodu oku ve tahmin et
↓
çalıştır ve gözlemle
↓
sınırları ve hataları kontrol et
↓
küçük fonksiyonlara ayır
↓
veriyi uygun yapıda tut
↓
sonucu doğrula
Öğrendiğimiz Python araçları bu yaklaşımı uygulamak için kullandığımız araçlardır:
- değişkenler ve veri türleri,
- ifadeler ve tür dönüşümleri,
- koşullar,
whilevefor,- fonksiyonlar,
- string,
- liste, demet, sözlük ve küme,
- dosya ve CSV,
- istisnalar ve modüller.
Daha ileri derslerde yeni yapılar öğreneceksiniz. Ancak temel alışkanlık aynı kalacaktır: kodu yalnızca yazmak değil, davranışını açıklamak ve doğrulamak.
Kendinizi kontrol edin
- Mikro uygulamada neden ham kaydı doğrudan hesaplamaya sokmak yerine önce dönüştürüyoruz?
kaydi_coz()neden geçersiz kayıttaNonedöndürüyor?continuerapor döngüsünde hangi amaçla kullanılıyor?urun_adetleri.get(urun, 0) + adetkalıbı ne yapıyor?- Neden
satir_toplami()ayrı bir fonksiyon olabilir? - Bir mantık hatasında bütün programı değiştirmek yerine ara değerleri izlemek neden daha iyi yaklaşımdır?
assertkontrolleri yeni özellikten sonra ne işe yarar?- “Ürün adı boşsa kayıt geçersiz” özelliğini eklemeden önce hangi örnek girdiyi hazırlardınız?
- Bu mikro uygulamada ilk 13 haftadan en az beş farklı kavramın nerede kullanıldığını açıklayın.
- Programın doğru sonuç vermesi ile kodun anlaşılır ve doğrulanabilir olması neden aynı şey değildir?