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 ve aralarındaki bağlantıyı görünür hâle getirmektir.
Mikro uygulamamız bir kampüs kırtasiye satış raporu olacak. Bu kez veri kodun içine gömülü bir listeden gelmeyecek: CSV dosyasını okuyacak, ham kayıtları dönüştürecek, geçersiz kayıtları ayıracak, rapor üretecek ve temel davranışları küçük testlerle doğrulayacağız.
Bu uygulamada özellikle şunları yeniden kullanacağız:
- String temizleme ve Türkçe metin normalleştirme,
csv.DictReaderile dosyadan veri okuma,- Tür dönüşümü ve
try/except, Noneile başarısız dönüşümü bildirme,- Liste ve sözlüklerle kayıt işleme,
- Sayaç, biriktirici, gruplama ve maksimum arama kalıpları,
- Fonksiyonlar, modüller ve main guard,
assertile küçük regresyon kontrolleri,- F-string ile iki ondalık basamaklı raporlama.
Bu bölümün kapsamı
- dönemin tamamı; final bu bölümün kapsamıyla sınırlı değildir
- kodu anlamlı modüllere ayırmanın gerekçesi
- CSV alanlarını güvenli çözme ve geçersiz kaydı ayıklama
- Türkçe metin normalleştirmesinin gruplamaya etkisi
- “veri yok” durumunu
Noneile raporlama - değişiklik sonrası eski doğrulama kontrollerini yeniden çalıştırma
- üretilen kodu kabul ölçütleriyle değerlendirme
- örnek uygulamanın dosya ve fonksiyon adlarının birebir ezberi
- uygulamaya eklenebilecek ileri özelliklerin tasarımı
Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.
Bütün bu araçlar tek bir programda nasıl birleşir?
Dönem boyunca her aracı ayrı ayrı gördük: tür dönüşümü bir bölümde, sözlük başka bir bölümde. Gerçek bir programda hepsi aynı anda ve birbirine bağlı çalışır. Bu bölümün konusu tam olarak o bağlantı.
Problem ve veri dosyası
Kırtasiye satışları satislar.csv adlı bir dosyada tutuluyor. Türkçe Excel kurulumlarında sık görülen bir biçimi özellikle kullanalım: alanlar noktalı virgülle ayrılmış, ondalık değerlerde virgül kullanılmış olsun.
urun;fiyat;adet
Kalem ;12,5;3
Defter;45;2
İĞNE;5,5;4
iğne;5,5;2
Silgi;8;abc
Cetvel;10;0
Bu küçük veri kümesinde bilinçli olarak birkaç durum var:
" Kalem "başta ve sonda boşluk içeriyor.İĞNEveiğneaynı ürün olarak gruplanmalı."12,5"ve"5,5"doğrudanfloat()ile dönüştürülemez; kontrollü biçimde ondalık virgülü ele almamız gerekir."abc"geçerli adet değildir.0adetli satış, bu uygulamanın iş kuralına göre geçersizdir.
Beklenen özet:
Geçerli kayıt sayısı: 4
Geçersiz kayıt sayısı: 2
Toplam ciro: 160.50 TL
Ürün adetleri: {'kalem': 3, 'defter': 2, 'iğne': 6}
Ciroya en fazla katkı: defter (90.00 TL)
Bu örnekte 12,5 gibi tek ondalık virgül içeren kontrollü veriyi replace(",", ".") ile dönüştüreceğiz. 1.234,56 gibi daha karmaşık yerel sayı biçimleri için bu basit yöntem genel çözüm değildir.
Uygulamanın dosya yapısı
Bu bölümde önceki haftalarda ayrı ayrı gördüğümüz dosya, modül ve test fikirlerini aynı küçük proje içinde birleştiriyoruz:
hafta14/
├── satislar.csv
├── veri.py
├── rapor.py
├── main.py
└── test_kontrolleri.py
Akışın zihinsel modeli:
satislar.csv
↓
veri.py: CSV'yi oku
↓
rapor.py: temizle → dönüştür → doğrula → özetle
↓
main.py: programı çalıştır ve raporu yazdır
↓
test_kontrolleri.py: temel davranışları doğrula
Buradaki amaç “çok dosyalı proje yapmak” değildir. Amaç sorumlulukları ayırmaktır: veriyi alma, hesaplama ve programı başlatma farklı işlerdir.
veri.py: CSV kayıtlarını okumak
- haftadaki Excel/CSV ayrıntılarını burada gerçekten kullanıyoruz:
import csv
def kayitlari_oku(dosya_yolu):
kayitlar = []
with open(
dosya_yolu,
"r",
encoding="utf-8-sig",
newline=""
) as dosya:
okuyucu = csv.DictReader(dosya, delimiter=";")
for satir in okuyucu:
kayitlar.append(satir)
return kayitlarBurada veri.py henüz sayısal dönüşüm yapmıyor. CSV’den gelen ham alanları string olarak okuyor ve dönüştürme sorumluluğunu rapor.py dosyasına bırakıyor.
encoding="utf-8-sig", dosyanın başında BOM varsa ilk sütun adının\ufeffurunolmasını engeller.delimiter=";", noktalı virgülle ayrılmış CSV’yi doğru sütunlara böler.newline="",csvmodülünün satır sonlarını kendisinin yönetmesini sağlar.
Türkçe ürün adını normalleştirmek
- haftada gördüğümüz önemli noktayı uygulamanın içine taşıyalım. Yalnızca
.lower()kullanmak Türkçeİiçin beklenmedik sonuç verebilir.
def urun_adi_temizle(urun):
donusum = str.maketrans({"I": "ı", "İ": "i"})
return urun.strip().translate(donusum).lower()Kontroller:
assert urun_adi_temizle(" Kalem ") == "kalem"
assert urun_adi_temizle("İĞNE") == "iğne"
assert urun_adi_temizle("iğne") == "iğne"
assert urun_adi_temizle("ISPARTA") == "ısparta"Böylece İĞNE ve iğne raporda iki ayrı anahtar hâline gelmez.
Bu normalleştirme Türkçe metin iş kuralına göre tasarlanmıştır. E-posta, kullanıcı adı veya uluslararası ürün kodlarında aynı dönüşüm otomatik olarak doğru kabul edilmemelidir.
Sayısal alanları güvenli çözmek
Fiyat için 0 değerini kabul edeceğiz; bu nedenle fonksiyonun adını sözleşmeyle uyumlu seçelim:
def negatif_olmayan_sayi_coz(metin):
try:
sayi = float(metin.strip().replace(",", "."))
except (ValueError, AttributeError):
return None
if sayi < 0:
return None
return sayiAdet ise gerçekten pozitif tam sayı olmalı; 0 adetli satış geçersizdir:
def pozitif_tam_sayi_coz(metin):
try:
sayi = int(metin.strip())
except (ValueError, AttributeError):
return None
if sayi <= 0:
return None
return sayiBu adlandırma önemlidir. Önceki sürümde pozitif_sayi_coz("0") değeri 0 döndürüyor olsaydı, fonksiyon adı ile davranış arasında sözleşme çelişkisi oluşurdu.
None ve is None
- haftada
Nonedeğerini “sonuç üretilemedi” sinyali olarak kullandık. Burada aynı kalıp var:
fiyat = negatif_olmayan_sayi_coz("abc")
if fiyat is None:
print("Fiyat geçersiz")None özel ve tekil bir Python nesnesidir. Bu nedenle None kontrolünde yaygın ve önerilen yazım:
if deger is None:
...ve tersi:
if deger is not None:
...şeklindedir.
Bir fonksiyon geçersizlik için None döndürüyorsa çağıran kodun bunu kontrol etmesi gerekir. Kontrol edilmezse sorun daha sonra, örneğin None * 3 gibi daha anlaşılmaz bir yerde ortaya çıkabilir.
Bir ham kaydı dönüştürmek
Şimdi tek bir CSV kaydını program içi kayda dönüştürelim:
def kaydi_coz(kayit):
urun_metni = kayit.get("urun")
fiyat_metni = kayit.get("fiyat")
adet_metni = kayit.get("adet")
if urun_metni is None or fiyat_metni is None or adet_metni is None:
return None
urun = urun_adi_temizle(urun_metni)
fiyat = negatif_olmayan_sayi_coz(fiyat_metni)
adet = pozitif_tam_sayi_coz(adet_metni)
if urun == "" or fiyat is None or adet is None:
return None
return {
"urun": urun,
"fiyat": fiyat,
"adet": adet
}Burada üç ayrı doğrulama türü birleşiyor:
- Beklenen sütun var mı?
- Ürün adı boş mu?
- Sayısal alanlar sözleşmeye uygun mu?
Tahmin et — hangi kayıtlar geçerli?
Aşağıdaki kodu çalıştırmadan önce her satırın sonucunu tahmin edin:
İlk iki kayıt sözlüğe dönüşmeli; son iki kayıt None üretmelidir.
Satır toplamı ve ürün bazında ciro
Geçerli bir kaydın ciro katkısı:
def satir_toplami(kayit):
return kayit["fiyat"] * kayit["adet"]Aynı ürünü hem adet hem ciro bakımından gruplayacağız:
urun_adetleri[urun] = urun_adetleri.get(urun, 0) + adet
urun_cirolari[urun] = urun_cirolari.get(urun, 0) + satir_cirosuBu, 10. ve 11. haftalardaki sözlük biriktirme kalıbının doğrudan uygulamasıdır.
Maksimum arama kalıbını yeniden kullanmak
Ciroya en fazla katkı yapan ürünü hazır max() kullanmadan, daha önce öğrendiğimiz maksimum arama kalıbıyla bulalım:
def en_yuksek_cirolu_urun(urun_cirolari):
if len(urun_cirolari) == 0:
return None
en_urun = None
en_ciro = None
for urun, ciro in urun_cirolari.items():
if en_ciro is None or ciro > en_ciro:
en_urun = urun
en_ciro = ciro
return en_urun, en_ciroBoş sözlükte anlamlı bir maksimum olmadığı için None döndürüyoruz. Bu tercih, “veri yok” durumunu 0 gibi gerçek bir sayısal sonuçla karıştırmaz.
rapor.py: ana rapor fonksiyonu
Parçaları birleştirelim:
def satis_raporu(ham_kayitlar):
toplam_ciro = 0
gecerli_sayisi = 0
gecersiz_sayisi = 0
urun_adetleri = {}
urun_cirolari = {}
for ham_kayit in ham_kayitlar:
kayit = kaydi_coz(ham_kayit)
if kayit is None:
gecersiz_sayisi = gecersiz_sayisi + 1
continue
gecerli_sayisi = gecerli_sayisi + 1
urun = kayit["urun"]
adet = kayit["adet"]
satir_cirosu = satir_toplami(kayit)
toplam_ciro = toplam_ciro + satir_cirosu
urun_adetleri[urun] = urun_adetleri.get(urun, 0) + adet
urun_cirolari[urun] = urun_cirolari.get(urun, 0) + satir_cirosu
return {
"gecerli": gecerli_sayisi,
"gecersiz": gecersiz_sayisi,
"ciro": toplam_ciro,
"urun_adetleri": urun_adetleri,
"urun_cirolari": urun_cirolari,
"lider": en_yuksek_cirolu_urun(urun_cirolari)
}Bu fonksiyonda yeni bir sözdizimi yoktur. Önceki haftalardan gelen kalıplar birleşmiştir:
forile kayıtları dolaşma,Nonekontrolü,continue,- Sayaç ve biriktirici,
- Sözlükle gruplama,
- Fonksiyon çağrıları,
- Maksimum arama.
Tarayıcıda bütün akışı deneyin
Gerçek projede satislar.csv dosyasını open() ile okuyacağız. Tarayıcı hücresinde ise aynı CSV metnini StringIO ile simüle edebiliriz:
Beklenen ana sonuçlar:
Geçerli kayıt sayısı: 4
Geçersiz kayıt sayısı: 2
Toplam ciro: 160.50 TL
Ürün adetleri: {'kalem': 3, 'defter': 2, 'iğne': 6}
Ciro lideri: ('defter', 90.0)
main.py: dosya hatasını yönetmek ve raporlamak
Gerçek dosyalarla çalışan ana program:
from veri import kayitlari_oku
from rapor import satis_raporu
def main():
try:
kayitlar = kayitlari_oku("satislar.csv")
except FileNotFoundError as hata:
print("Satış dosyası bulunamadı:", hata)
return
rapor = satis_raporu(kayitlar)
print("Geçerli kayıt sayısı:", rapor["gecerli"])
print("Geçersiz kayıt sayısı:", rapor["gecersiz"])
print(f"Toplam ciro: {rapor['ciro']:.2f} TL")
print("Ürün adetleri:", rapor["urun_adetleri"])
if rapor["lider"] is not None:
urun, ciro = rapor["lider"]
print(f"Ciroya en fazla katkı: {urun} ({ciro:.2f} TL)")
if __name__ == "__main__":
main()Burada 13. haftadaki iki fikir birlikte görülür:
FileNotFoundErrorbeklenen bir dosya problemi olarak ele alınır.- Main guard sayesinde
main.pydoğrudan çalıştırıldığındamain()çağrılır.
Küçük regresyon kontrolleri
Kod çalışıyor görünse bile kritik davranışları açık beklentilerle kontrol edelim:
assert urun_adi_temizle("İĞNE") == "iğne"
assert urun_adi_temizle("iğne") == "iğne"
assert negatif_olmayan_sayi_coz("12,5") == 12.5
assert negatif_olmayan_sayi_coz("0") == 0
assert negatif_olmayan_sayi_coz("-1") is None
assert negatif_olmayan_sayi_coz("abc") is None
assert pozitif_tam_sayi_coz("3") == 3
assert pozitif_tam_sayi_coz("0") is None
assert pozitif_tam_sayi_coz("-2") is None
assert kaydi_coz({"urun": " ", "fiyat": "10", "adet": "1"}) is None
rapor = satis_raporu([
{"urun": "İĞNE", "fiyat": "5,5", "adet": "4"},
{"urun": "iğne", "fiyat": "5,5", "adet": "2"},
])
assert rapor["gecerli"] == 2
assert rapor["gecersiz"] == 0
assert rapor["urun_adetleri"]["iğne"] == 6
assert abs(rapor["ciro"] - 33.0) < 0.001Son satırda doğrudan == yerine tolerans kullanılması, 7. haftada gördüğümüz kayan noktalı sayı testleriyle aynı ilkedir.
Hata ayıklama senaryosu
Program beklenen 160.50 TL yerine başka bir sonuç verirse bütün kodu aynı anda değiştirmeyin.
Şu sırayı izleyin:
- CSV’de kaç satır olduğunu kontrol edin.
kayitlari_oku()sonrasında ilk birkaç ham kaydı inceleyin.- Her ham kayıt için
kaydi_coz()sonucunu kontrol edin. SilgiveCetvelkayıtlarının neden geçersiz olduğunu doğrulayın.İĞNEveiğnedeğerlerinin aynı anahtara dönüştüğünü kontrol edin.- Her geçerli kayıt için
satir_toplami()sonucunu hesaplayın. urun_cirolarisözlüğünün her turdaki durumunu izleyin.- Tek bir hipotezi sınayacak küçük değişiklik yapın.
- Tüm
assertkontrollerini yeniden çalıştırın.
Bu süreç, 13. haftadaki sistematik debugging yaklaşımını gerçek bir veri akışına uygular.
AI ile kontrollü kod inceleme
Bu aşamada AI’den küçük bir değişiklik veya alternatif çözüm istemek mümkündür; fakat çıktıyı doğrudan kabul etmek dersin hedefi değildir.
Örneğin AI şu değişikliği önerebilir:
def pozitif_tam_sayi_coz(metin):
sayi = int(metin)
if sayi < 0:
return None
return sayiİlk bakışta kısa ve makul görünüyor. Ancak kabul kriterlerimize göre iki sorun vardır:
"abc"içinValueErrorüretir; kontrollü başarısızlık yoktur.0değeri kabul edilir; oysa bu uygulamada adet pozitif olmalıdır.
Dolayısıyla AI çıktısını değerlendirirken şu üç soruyu kullanın:
- Sözleşmeye uyuyor mu?
- Sınır ve geçersiz durum testlerini geçiyor mu?
- Mevcut davranışlardan birini bozuyor mu?
AI’nin ürettiği kodun daha kısa veya daha “profesyonel” görünmesi, doğru olduğu anlamına gelmez. Bu derste kabul ölçütü açıklanabilir ve doğrulanmış davranıştır.
Sınırlı özellik ekleme
Hazır projeye aşağıdakilerden yalnızca birini eklemeyi deneyin:
- Fiyatı
0olan kayıtları ayrıca saymak, - Belirli bir ciro eşiğinin üzerindeki ürünleri listelemek,
- Raporu
rapor.txtdosyasına yazmak, - Geçersiz kayıtların nedenlerini ayrı sayaçlarda toplamak,
- Aynı ürün için farklı fiyatlar gelirse bunu uyarı olarak raporlamak.
Özellik eklemeden önce kabul kriterini yazın ve en az bir başarısız örnek hazırlayın.
Örneğin:
Aynı ürün iki farklı fiyatla gelirse rapor bu ürünü "fiyat tutarsızlığı" olarak işaretlemeli.
Sonra veri örneği:
[
{"urun": "Kalem", "fiyat": "10", "adet": "1"},
{"urun": "Kalem", "fiyat": "12", "adet": "1"},
]Ancak bundan sonra kodu değiştirin.
Yerel çalışma dosyaları
Bu bölümdeki çok dosyalı örneği kendi bilgisayarınızda çalıştırmak için proje dosyaları ders deposunda birlikte verilmiştir:
Aynı klasörde terminal açıp önce:
python test_kontrolleri.py
ardından:
python main.py
çalıştırabilirsiniz. Windows’ta kurulumunuza göre python yerine py kullanabilirsiniz.
Ders sonu kontrolü
Mikro uygulama için şu sorulara cevap verebilmelisiniz:
satislar.csvverisi neden doğrudan hesaplamaya sokulmuyor?utf-8-sigvedelimiter=";"hangi gerçek dosya problemlerini çözüyor?urun_adi_temizle()neden yalnızca.lower()kullanmıyor?negatif_olmayan_sayi_coz()ilepozitif_tam_sayi_coz()sözleşmeleri neden farklıdır?Noneburada ne anlama geliyor ve nedenis Noneile kontrol ediliyor?0adetli satış neden bu uygulamada geçersiz kabul edildi?urun_adetleriveurun_cirolarisözlükleri hangi biriktirme kalıbını kullanıyor?- Maksimum ciro ürünü nasıl bulunuyor?
FileNotFoundErrorhangi modülde ve neden ele alınıyor?- AI tarafından üretilen bir değişikliği kabul etmeden önce hangi testleri çalıştırırsınız?
Dönem özeti
Bu ders boyunca programlamayı yalnızca Python sözdizimi olarak ele almadık. Şu çalışma biçimini geliştirdik:
problemi anla
↓
girdi–işlem–çıktıyı belirle
↓
kabul kriterlerini ve sınırları düşün
↓
küçük çözüm adımları oluştur
↓
kodu oku ve tahmin et
↓
çalıştır ve gözlemle
↓
hataları ve veri kalitesini kontrol et
↓
küçük fonksiyonlara ve gerekirse modüllere ayır
↓
veriyi uygun yapıda tut
↓
sonucu testlerle 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,
- İfadeler ve tür dönüşümleri,
- Koşullar,
whilevefor,- Fonksiyonlar, kapsam ve varsayılan parametreler,
- String işlemleri ve metin normalleştirme,
- Liste, demet, sözlük ve küme,
- Dosya ve CSV,
- İstisnalar, modüller ve main guard,
assert, sınır durumları ve sistematik hata ayıklama.
Daha ileri derslerde yeni yapılar ve daha güçlü geliştirme araçları göreceksiniz. AI ve coding agent’lar da daha fazla sorumluluk üstlenebilir. Ancak temel ilke değişmez: üretilen kodu anlayabilmek, davranışını açıklayabilmek ve doğrulayabilmek.
Bu bölümün kazanımları
Bu bölümü bitiren öğrenci:
- Dönem boyunca öğrenilen araçları tek bir küçük uygulamada birlikte kullanır.
- CSV alanlarını güvenli biçimde çözer ve geçersiz kaydı ayıklar.
- Türkçe metin normalleştirmesini ürün adlarına uygular.
- “Veri yok” durumunu
Noneile raporlar. - Değişiklikten sonra eski doğrulama kontrollerini yeniden çalıştırır.
- Üretilen kodu kabul ölçütleriyle değerlendirir.
- Rapor döngüsünde
continueveget()kalıplarını uygular.