Fonksiyon tasarımı ve doğrulama

Bir fonksiyon yazmak, yalnızca def satırını doğru yazmak değildir. Fonksiyonun hangi girdileri beklediğini, hangi sonucu ürettiğini ve yanlış kullanımda ne olacağını düşünmek gerekir.

Bu hafta fonksiyonları küçük birer sözleşme gibi ele alacağız. Ayrıca yerel/küresel kapsamı, varsayılan parametreleri ve assert kullanarak beklenen davranışı kontrol etmeyi; sınır değerlerini sistematik biçimde denemeyi öğreneceğiz.

Bu bölümün kapsamı

NoteSınavda sorulur
  • fonksiyon sözleşmesi ve ön koşullar
  • yerel değişkenin kapsamı ve dış bağımlılığı parametreye çevirme
  • varsayılan parametrenin davranışı
  • assert ile beklenen davranışı doğrulama
  • float sonuçlarda tam eşitlik yerine toleranslı karşılaştırma
  • sınır ve uç durumların seçilerek sınanması
  • saf fonksiyonun özellikleri ve global kullanımına temkin
  • unittest ve pytest gibi test çerçeveleri
  • tip ipuçları (type hints)

Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.

Bu fonksiyonun doğru çalıştığını nereden biliyorum?

Bir kez çalıştırıp doğru sonuç görmek yeterli değildir. Bu bölümde bir fonksiyonun ne zaman doğru sayılacağını önceden yazmayı ve bunu seçilmiş birkaç örnekle sınamayı öğreneceğiz.

Fonksiyon sözleşmesi

Bir fonksiyonun nasıl kullanılacağını düşünmenin basit yollarından biri üç sorudur:

  1. Ne alıyor?
  2. Ne yapıyor?
  3. Ne döndürüyor?

Örneğin:

def saat_ucreti(toplam_ucret, saat):
    return toplam_ucret / saat

Bu fonksiyon için şu sözleşmeyi yazabiliriz:

Girdi:
- toplam_ucret: 0 veya daha büyük sayı
- saat: 0'dan büyük sayı

Davranış:
- toplam_ucret değerini saat değerine böler

Çıktı:
- saat başına ücret

Burada saat > 0 önemli bir ön koşuldur. Çünkü sıfıra bölme yapılamaz.

Note

Fonksiyon sözleşmesi, Python’ın özel bir sözdizimi değildir. Fonksiyonun beklenen kullanımını ve sonucunu açık biçimde düşünme yöntemidir.

Ön koşul ve sonuç

Bir fonksiyonun doğru çalışması için bazı varsayımlar gerekebilir.

Örneğin yüzde hesabı:

def yuzde_hesapla(tutar, oran):
    return tutar * oran

Bu fonksiyon teknik olarak oran = 3 ile de çalışır. Fakat bizim problemimizde oranı 0 ile 1 arasında bekliyorsak bunu tasarımda açıkça belirtmeliyiz.

Ön koşul:
0 <= oran <= 1

Beklenen sonuç ise şöyle ifade edilebilir:

Sonuç:
tutar * oran değeri döndürülür

Bu açıklık, daha sonra test yazmayı kolaylaştırır.

Kapsam (scope): ad nerede geçerlidir?

  1. haftada fonksiyon içindeki değişkenlerin çoğunlukla fonksiyonun kendi çalışma alanında kaldığını söylemiştik. Şimdi bunu açıklaştıralım.
def cevre_hesapla(kenar):
    sonuc = kenar * 4
    return sonuc

print(cevre_hesapla(5))
# print(sonuc)  # NameError

sonuc, fonksiyonun içinde oluşturulan yerel (local) bir değişkendir. Fonksiyon çağrısı dışındaki kod bu yerel adı doğrudan kullanamaz.

Fonksiyon dışında oluşturulan bir ad ise küresel (global) kapsamda bulunabilir:

oran = 0.20

def kdv_hesapla(tutar):
    return tutar * oran

Bu kod çalışır; fonksiyon dışarıdaki oran değerini okuyabilir. Fakat fonksiyonun sonucunu anlamak için artık yalnızca parametrelerine değil, dışarıdaki bir değere de bakmamız gerekir.

Başlangıç düzeyinde tercihimiz çoğu hesaplama için bağımlılığı parametreyle açıkça vermektir:

def kdv_hesapla(tutar, oran):
    return tutar * oran

Bu sürüm, fonksiyonun hangi bilgiye ihtiyaç duyduğunu imzasında görünür kılar.

Important

Aynı isim fonksiyon içinde ve dışında kullanılabilir; bunların her zaman aynı değişken olduğunu varsaymayın. Bir adı nerede oluşturduğunuz ve nerede kullandığınız önemlidir. Bu derste ayrıntılı LEGB kapsam kurallarına girmiyoruz; yerel/küresel ayrımı ve parametre kullanım ilkesi yeterlidir.

Fonksiyon içinde oluşturulan yerel değişkenin yalnızca o fonksiyon çağrısının kapsamında bulunması.

Yerel kapsam

Varsayılan parametreler

Bazı fonksiyonlarda bir parametrenin çoğu çağrıda aynı değeri kullanması mantıklı olabilir. Bu durumda tanımda varsayılan değer (default value) verebiliriz:

def indirimli_fiyat(fiyat, oran=0.10):
    return fiyat - fiyat * oran

İkinci argüman verilmezse varsayılan %10 kullanılır:

print(indirimli_fiyat(1000))       # 900.0

İhtiyaç olduğunda çağrıdaki argüman varsayılanı değiştirir:

print(indirimli_fiyat(1000, 0.25)) # 750.0

Bu fonksiyonun sözleşmesinde varsayılanı açıkça belirtmek gerekir:

fiyat: indirim uygulanacak tutar
oran: indirim oranı; verilmezse 0.10
Note

Zorunlu parametreler, başlangıç düzeyinde varsayılan değerli parametrelerden önce yazılır: def f(a, b=1): .... def f(a=1, b): ... geçerli değildir.

Varsayılan değer, gerçekten makul bir varsayılan davranış varsa kullanılmalıdır. Önemli bir iş kuralını çağıranın fark etmeyeceği biçimde gizlemek için varsayılan parametre eklemeyin.

assert ile beklentiyi kontrol etmek

assert, doğru olmasını beklediğimiz bir koşulu kontrol eder.

sonuc = 2 + 3
assert sonuc == 5

Koşul True ise program sessizce devam eder. Koşul False ise AssertionError oluşur.

Fonksiyon davranışını küçük örneklerle doğrulayabiliriz:

def kare(sayi):
    return sayi * sayi

assert kare(3) == 9
assert kare(0) == 0
assert kare(-2) == 4

Bu üç kontrol farklı durumları kapsar.

Important

assert, kullanıcıdan gelen hatalı girdiyi yönetmek için bir arayüz değildir. Burada onu geliştiricinin “bu koşul doğru olmalı” beklentisini doğrulamak için kullanıyoruz. 13. haftada geçersiz koşulları çalışma zamanında açıkça bildirmek için istisna üretmeyi (raise) ayrıca göreceğiz.

Fonksiyon sözleşmesi ile normal, sınır ve geçersiz durum testlerinin ilişkisi.

Fonksiyon sözleşmesi ve test

float sonuçlarında tam eşitlik her zaman uygun değildir

  1. haftada bazı ondalık sayıların float olarak yaklaşık temsil edildiğini görmüştük. Bu nedenle şu beklenti başarısız olabilir:
# assert 0.1 + 0.2 == 0.3

Çünkü 0.1 + 0.2 tipik olarak 0.30000000000000004 değerine çok yakındır, ama 0.3 ile bit düzeyinde aynı temsil olmayabilir.

Bir hesapta küçük bir sayısal fark kabul edilebiliyorsa sonuç ile beklenen değer arasındaki farkı bir tolerans ile sınayabiliriz:

sonuc = 0.1 + 0.2
beklenen = 0.3

assert abs(sonuc - beklenen) < 0.000001

Para bağlamında örneğin bir kuruştan küçük farkı kabul etmek istiyorsak problem kuralına göre şöyle bir tolerans seçilebilir:

assert abs(sonuc - beklenen) < 0.01
Warning

Toleransın değeri “her zaman 0.01 kullanın” biçiminde evrensel bir kural değildir. Kabul edilebilir farkı problem alanı belirler. Ayrıca ekranda iki ondalık basamak göstermek, hesaplamayı otomatik olarak kesin ondalık aritmetiğe dönüştürmez.

Tahmin et — hangi assert başarısız olur?

Aşağıdaki kodu çalıştırmadan önce hangi satırın hata üreteceğini tahmin edin:

Bu sürümde üç kontrol de geçer. Şimdi ilk if satırını if sayi <= 0: yaparsanız davranış değişir mi? Tahmin edin ve deneyin.

Sınır değerlerini fonksiyonlarda kullanmak

Koşul içeren fonksiyonlarda sınır değerleri özellikle önemlidir.

Bir ücretsiz kargo fonksiyonu düşünelim:

def kargo_ucreti(sepet):
    if sepet >= 500:
        return 0
    return 39.90

Önemli testler yalnızca 100 ve 1000 değildir:

assert kargo_ucreti(499) == 39.90
assert kargo_ucreti(500) == 0
assert kargo_ucreti(501) == 0

500 tam kararın değiştiği noktadır.

Alıştırma — sınır testini düzeltin

Aşağıdaki fonksiyonda geçme sınırı 60 olmalıdır. Mevcut kod sınır değerini yanlış ele alıyor.

Fonksiyonu üç assert de geçecek biçimde düzeltin.

> yerine >= kullanmayı düşünün.

def gecti_mi(puan):
    return puan >= 60

assert gecti_mi(59) == False
assert gecti_mi(60) == True
assert gecti_mi(61) == True

print("Kontroller tamamlandı")

Bir test tek başına yeterli değildir

Şu fonksiyona bakalım:

def indirim(fiyat):
    if fiyat >= 1000:
        return fiyat * 0.90
    return fiyat

Sadece şu testi yazarsak:

assert indirim(1500) == 1350

fonksiyonun düşük fiyatlarda doğru çalıştığını bilmiyoruz.

Daha dengeli testler:

assert indirim(500) == 500
assert indirim(999) == 999
assert indirim(1000) == 900
assert indirim(1500) == 1350

Burada:

  • Normal durum,
  • Sınırın hemen altı,
  • Sınırın kendisi,
  • Sınırın üstü

kontrol edilir.

Uç durum (edge case)

Her problemde sınır değeri sayısal bir eşik olmak zorunda değildir.

Örneğin metnin uzunluğunu döndüren fonksiyon:

def karakter_sayisi(metin):
    return len(metin)

Boş metin önemli bir uç durumdur (edge case):

assert karakter_sayisi("") == 0
assert karakter_sayisi("Python") == 6

Bir liste fonksiyonunda boş liste, bir dosya işleminde boş dosya veya bir arama probleminde hiç eşleşme bulunmaması benzer uç durumlar olabilir.

Saf fonksiyon fikri

Aşağıdaki fonksiyon yalnızca argümanlarını kullanır ve bir değer döndürür:

def cevre(uzunluk, genislik):
    return 2 * (uzunluk + genislik)

Aynı argümanlarla çağrıldığında aynı sonucu verir ve dışarıdaki bir değeri değiştirmez. Bu tür fonksiyonlara saf fonksiyon (pure function) denir.

Saf fonksiyonların önemli bir avantajı test edilmelerinin kolay olmasıdır:

assert cevre(4, 6) == 20
assert cevre(1, 1) == 4

Karşılaştırmak için:

toplam = 0

def ekle(deger):
    global toplam
    toplam = toplam + deger

Bu fonksiyon dışarıdaki toplam değişkenini değiştirir. Bu bir yan etki (side effect) örneğidir.

Buradaki global toplam satırı, fonksiyon içindeki atamanın yeni bir yerel toplam oluşturması yerine dışarıdaki küresel toplam değişkenini değiştirmesine izin verir. global bu örnekte önerilen çözüm kalıbı olarak değil, yan etkiyi görünür kılmak için kullanılıyor.

Warning

Bu derste hesaplama fonksiyonlarında global kullanmayı varsayılan çözüm olarak benimsemeyeceğiz. Bir fonksiyonun bilgiye ihtiyacı varsa onu parametreyle almak; sonuç üretiyorsa return ile döndürmek genellikle bağımlılıkları daha açık ve test edilebilir kılar.

Başlangıç programlarında her şeyi saf fonksiyon yapmak zorunda değiliz; ancak hesaplama yapan fonksiyonlarda mümkün olduğunca argüman alıp değer döndürmek iyi bir varsayılan yaklaşımdır.

Tip

Bir fonksiyonun davranışını anlamak için programın başka yerlerindeki değişkenleri takip etmek zorunda kalıyorsanız fonksiyon gereğinden fazla dış duruma bağımlı olabilir.

Alıştırma — saf hesaplama fonksiyonu

Aşağıdaki fonksiyon, kilometre cinsinden mesafeyi metreye çevirmelidir.

Fonksiyonu tamamlayın.

return km * 1000 yeterlidir.

def metreye_cevir(km):
    return km * 1000

assert metreye_cevir(1) == 1000
assert metreye_cevir(0) == 0
assert metreye_cevir(2.5) == 2500

print("Kontroller tamamlandı")

Fonksiyon tek bir işi mi yapıyor?

Aşağıdaki fonksiyon çok farklı sorumlulukları bir araya getiriyor:

def siparis_isle(fiyat, adet):
    toplam = fiyat * adet
    if toplam >= 500:
        kargo = 0
    else:
        kargo = 39.90
    print("Ürün toplamı:", toplam)
    print("Kargo:", kargo)
    print("Genel toplam:", toplam + kargo)

Bu kod çalışabilir; fakat hesaplama ile çıktı üretme iç içedir.

Daha küçük parçalar düşünebiliriz:

def urun_toplami(fiyat, adet):
    return fiyat * adet

def kargo_ucreti(tutar):
    if tutar >= 500:
        return 0
    return 39.90

Sonra programın ana akışı bunları kullanabilir:

toplam = urun_toplami(120, 3)
kargo = kargo_ucreti(toplam)
print("Genel toplam:", toplam + kargo)

Bu tasarımda iki hesaplamayı ayrı ayrı doğrulayabiliriz.

İncele ve düzelt — yanlış formül

Aşağıdaki fonksiyon Celsius sıcaklığını Fahrenheit’a çevirmeyi amaçlıyor:

Formülde eksik olan kısmı bulun ve düzeltin.

Formül: celsius * 9 / 5 + 32.

def fahrenheit(celsius):
    return celsius * 9 / 5 + 32

assert fahrenheit(0) == 32
assert fahrenheit(100) == 212

Üret — aidat gecikme hesabı

Bir apartman yönetiminde aylık aidatın zamanında ödenmediği her gün için 2.5 TL gecikme bedeli eklendiğini varsayalım.

gecikmeli_tutar(aidat, geciken_gun) adlı bir fonksiyon yazın.

Kurallar:

  • geciken_gun 0 ise yalnızca aidat döndürülmeli.
  • Her gecikme günü için 2.5 TL eklenmeli.
  • Fonksiyon yalnızca hesaplama yapıp sonuç döndürmeli; içinde print() kullanılmamalı.

Başlangıç:

Toplam gecikme bedeli geciken_gun * 2.5 olur.

def gecikmeli_tutar(aidat, geciken_gun):
    return aidat + geciken_gun * 2.5

assert gecikmeli_tutar(500, 0) == 500
assert gecikmeli_tutar(500, 1) == 502.5
assert gecikmeli_tutar(500, 4) == 510

Doğrulama için küçük bir kontrol listesi

Bir fonksiyonu tamamladıktan sonra şu sorular yararlıdır:

  • Fonksiyonun tek ve açık bir işi var mı?
  • Parametrelerin neyi temsil ettiği anlaşılır mı?
  • Varsayılan parametre varsa neden o değerin seçildiği açık mı?
  • Hangi girdilerin geçerli olduğu belli mi?
  • Dönüş değerinin anlamı açık mı?
  • Normal bir örnek denendi mi?
  • Sınır değeri denendi mi?
  • Uç durum varsa denendi mi?
  • float sonucu karşılaştırırken tam eşitlik gerçekten uygun mu?
  • Hesaplama fonksiyonu gereksiz print(), global veya dış değişken kullanıyor mu?

Bu soruların hepsi her fonksiyonda aynı derecede önemli olmayabilir. Ama bir hata olduğunda rastgele değer denemek yerine sistematik düşünmeye yardım eder.

Sınav provası

Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. Bir fonksiyonun sözleşmesi hangi üç soruya cevap verir?

  • A. Hangi dosyada yer alır, kim yazmıştır ve ne zaman yazılmıştır
  • B. Ne kadar hızlı çalışır, ne kadar bellek kullanır ve kaç kez çağrılır
  • C. Hangi girdileri kabul eder, ne yapar ve ne döndürür
  • D. Hangi hataları verir, hangi sürümde çalışır ve hangi modülü kullanır
  • E. Kaç satır uzunluktadır, hangi döngüyü kullanır ve kaç değişken tanımlar

Madde 2. Ön koşul nedir?

  • A. Fonksiyondan önce çalıştırılması gereken başka bir fonksiyondur
  • B. Fonksiyonun kaç parametre alacağını belirleyen kuraldır
  • C. Fonksiyonun ilk satırında yazılması zorunlu bir açıklamadır
  • D. Fonksiyonun doğru çalışması için girdilerin sağlaması gereken şarttır
  • E. Fonksiyonun döndüreceği değerin türüdür

Madde 3. Aşağıdaki program neden hata verir?

def hesapla():
    sonuc = 42

hesapla()
print(sonuc)
  • A. sonuc ayrılmış bir sözcüktür
  • B. 42 sayısı bir değişkene atanamaz
  • C. Fonksiyon çağrılmadığı için sonuc hiç oluşmamıştır
  • D. print() fonksiyon dışında kullanılamaz
  • E. sonuc fonksiyonun yerel değişkenidir; fonksiyon bitince yok olur ve dışarıdan erişilemez

Madde 4. def indirim(fiyat, oran=0.10): tanımında indirim(200) çağrısında oran hangi değeri alır?

  • A. 0.10
  • B. None
  • C. 200
  • D. 0
  • E. Hata verir; iki parametreli fonksiyon tek argümanla çağrılamaz

Madde 5. def basarili_mi(puan, sinir=50): tanımıyla geçme sınırını 60 yapmak için hangi çağrı doğrudur?

  • A. basarili_mi(60)
  • B. basarili_mi(55, sinir=60)
  • C. sinir = 60 satırını fonksiyonun dışında yazmak
  • D. basarili_mi(sinir=60)
  • E. basarili_mi(55, 50, 60)

Madde 6. assert ortalama([60, 80]) == 70 satırı ne yapar?

  • A. Sonucu ekrana yazar
  • B. Fonksiyonu iki kez çalıştırıp sonuçları karşılaştırır
  • C. Koşul doğruysa sessizce geçer, yanlışsa AssertionError vererek programı durdurur
  • D. Fonksiyonu düzeltir
  • E. Değeri 70 olarak zorlar

Madde 7. assert 0.1 + 0.2 == 0.3 satırı neden başarısız olabilir?

  • A. Python assert içinde toplama yapılmasına izin vermez
  • B. 0.3 bir float değil str olarak yorumlanır
  • C. Toplama işlemi yanlış yazılmıştır
  • D. float değerler yaklaşık tutulduğu için toplam 0.3e çok yakın ama tam eşit olmayabilir
  • E. assert ondalıklı sayılarla çalışmaz

Madde 8. Geçme sınırı 60 olan bir fonksiyon için 59, 60 ve 61 neden iyi bir test kümesidir?

  • A. Bu üç değer bütün olası girdileri temsil eder
  • B. Üç ardışık sayı seçmek her testte iyi bir kuraldır
  • C. Python sınır değerlerini kendiliğinden özel olarak işler
  • D. Küçük sayılarla test etmek daha hızlıdır
  • E. Sınırın hemen altını, kendisini ve hemen üstünü kapsar; > ile >= arasındaki karışıklık bu üç değerde ortaya çıkar

Madde 9. Boş metin bazı fonksiyonlarda neden uç durum sayılır?

  • A. Uzunluğu sıfır olduğu için indeksleme, ilk karakteri alma veya ortalama gibi işlemler beklenmedik biçimde davranabilir
  • B. Boş metin bellekte yer kaplamaz
  • C. len() boş metinde çalışmaz
  • D. Boş metin her zaman hata verir
  • E. Boş metin Python’da None ile aynıdır

Madde 10. Saf fonksiyonun iki temel özelliği nedir?

  • A. Yalnızca sayılarla çalışır ve döngü içermez
  • B. Yalnızca argümanlarını kullanır ve dış durumu değiştirmez; aynı girdiyle her zaman aynı sonucu döndürür
  • C. Yalnızca bir satırdan oluşur
  • D. Hiç parametre almaz ve hiç değer döndürmez
  • E. Ekrana yazdırır ve küresel değişkenleri günceller

Madde 1 — Doğru: C. Sözleşme, fonksiyonu kullanacak kişinin gövdeyi okumadan bilmesi gereken üç şeydir.

  • A yanlış: Bunlar belgeleme bilgileridir, davranış sözleşmesi değil.
  • B yanlış: Başarım ölçütleri ayrı bir konudur.
  • D yanlış: Bunlar yararlı ayrıntılar olabilir ama sözleşmenin çekirdeği girdi, davranış ve sonuçtur.
  • E yanlış: Bunlar gövdenin iç ayrıntılarıdır; çağıranı ilgilendirmez.

Madde 2 — Doğru: D. Örneğin ortalama hesaplayan bir fonksiyonun ön koşulu listenin boş olmamasıdır.

  • A yanlış: Çağrı sırası ayrı bir konudur; ön koşul girdinin taşıması gereken özelliktir.
  • B yanlış: Parametre sayısı imzada belirlenir; ön koşul değerlerin niteliğiyle ilgilidir.
  • C yanlış: Belgelemek yararlıdır ama zorunlu değildir; ön koşul bir davranış şartıdır.
  • E yanlış: Bu sonuçla ilgilidir; ön koşul girdiye ilişkindir.

Madde 3 — Doğru: E. Yerel adlar yalnızca kendi fonksiyonlarının içinde yaşar. Değeri dışarı taşımanın yolu returndur.

  • A yanlış: sonuc ayrılmış sözcük değildir.
  • B yanlış: Atama sorunsuzdur; hata print(sonuc) satırında çıkar.
  • C yanlış: Fonksiyon çağrılıyor; sorun adın kapsamı.
  • D yanlış: print() her yerde kullanılabilir.

Madde 4 — Doğru: A. Argüman verilmediğinde tanımdaki varsayılan kullanılır.

  • B yanlış: Varsayılan tanımlıysa None atanmaz.
  • C yanlış: 200 ilk parametreye, yani fiyata gider.
  • D yanlış: Varsayılan açıkça 0.10 yazılmış; sıfırlanmaz.
  • E yanlış: Varsayılanı olan parametre isteğe bağlıdır; çağrı geçerlidir.

Madde 5 — Doğru: B. Anahtar kelimeli argüman varsayılanın yerine geçer ve hangi parametreyi değiştirdiğinizi açıkça gösterir.

  • A yanlış: Bu, 60ı puan yapar; sınır yine 50 kalır.
  • C yanlış: Dışarıdaki bir ad, varsayılan değeri değiştirmez.
  • D yanlış: puan zorunlu bir parametredir; verilmezse TypeError çıkar.
  • E yanlış: Fonksiyon en fazla iki argüman alır; üçüncüsü hata verir.

Madde 6 — Doğru: C. Beklenen davranışı hızlıca sınamanın yoludur: sorun yoksa hiçbir çıktı üretmez.

  • A yanlış: assert doğru geçtiğinde hiçbir şey yazmaz.
  • B yanlış: Tek çağrı yapılır ve sonucu beklenen değerle karşılaştırılır.
  • D yanlış: Düzeltme yapmaz; yalnızca beklentiyi sınar.
  • E yanlış: Atama yapmaz; yalnızca eşitliği sınar.

Madde 7 — Doğru: D. Bu yüzden float sonuçlarda tam eşitlik yerine problem alanına uygun bir tolerans kullanılır.

  • A yanlış: İzin verir; assert her bool ifadeyi sınayabilir.
  • B yanlış: Tırnak yok; 0.3 bir floattur.
  • C yanlış: Yazımda sorun yok.
  • E yanlış: Çalışır; sorun karşılaştırmanın kendisinde.

Madde 8 — Doğru: E. Hatanın gizlenebileceği tek yer sınırın çevresidir; uzak değerler bu kusuru göstermez.

  • A yanlış: Temsil etmezler; geçersiz girdiler ayrıca sınanmalıdır.
  • B yanlış: Önemli olan ardışıklık değil, değerlerin sınırın çevresinde olması.
  • C yanlış: Özel bir işlem yoktur; sınır davranışını programcı belirler.
  • D yanlış: Hız bir ölçüt değildir.

Madde 9 — Doğru: A. Boş metinde metin[0] hata verir. Bu yüzden boş değer ayrıca düşünülmelidir.

  • B yanlış: Bellek kullanımı uç durum olmasının nedeni değildir.
  • C yanlış: len("") sonucu 0dır ve sorunsuz çalışır.
  • D yanlış: Birçok işlemde sorunsuz çalışır; sorun yalnızca belirli işlemlerde çıkar.
  • E yanlış: Aynı değildir; "" bir str, None ayrı bir değerdir.

Madde 10 — Doğru: B. Bu iki özellik fonksiyonu tek başına sınanabilir ve öngörülebilir kılar.

  • A yanlış: Tür ve iç yapı saflıkla ilgili değildir.
  • C yanlış: Uzunlukla ilgisi yoktur.
  • D yanlış: Tam tersi; saf fonksiyon girdisini parametreden alır ve sonucunu döndürür.
  • E yanlış: İkisi de dış etki üretir; saflığı bozan davranışlardır.

Tek sayfa özet

Bu hafta fonksiyonları yalnızca kod tekrarını azaltan yapılar olmaktan çıkarıp küçük, doğrulanabilir bileşenler olarak ele aldık.

  • Fonksiyon sözleşmesi, beklenen girdi, davranış ve sonucu açıklar.
  • Ön koşullar fonksiyonun hangi girdilerle anlamlı olduğunu belirtir.
  • Yerel değişken fonksiyonun kendi kapsamında yaşar; dış bağımlılıkları parametrelerle açık hâle getirmek çoğu hesaplama fonksiyonunu daha anlaşılır yapar.
  • Varsayılan parametre, argüman verilmediğinde kullanılacak açık bir varsayılan davranış sağlar.
  • global, fonksiyonun dışarıdaki küresel bir adı değiştirmesine izin verebilir; bu derste hesaplama fonksiyonlarında varsayılan çözüm olarak kullanılmaz.
  • assert, geliştiricinin beklediği bir davranışı hızlıca doğrulamak için kullanılabilir.
  • float sonuçlarında tam eşitlik yerine problem alanına uygun toleransla karşılaştırma gerekebilir.
  • Koşullu fonksiyonlarda sınır değerleri özellikle önemlidir.
  • Boş metin, boş koleksiyon veya hiç eşleşme bulunmaması gibi durumlar uç durum olabilir.
  • Saf fonksiyonlar argümanlarını kullanır, sonuç döndürür ve dış durumu değiştirmez.
  • Küçük ve tek sorumluluklu fonksiyonları ayrı ayrı doğrulamak daha kolaydır.

Bir sonraki bölümde fonksiyonlarla birlikte sık kullanacağımız metin (str) değerlerini daha ayrıntılı inceleyeceğiz.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Fonksiyon sözleşmesini ve ön koşulları açıklar.
  • Yerel değişkenin kapsamını bilir ve dış bağımlılığı parametreye çevirmeyi gerekçelendirir.
  • Varsayılan parametrenin davranışını uygular.
  • assert ile beklenen davranışı doğrular.
  • float sonuçlarda tam eşitlik yerine toleranslı karşılaştırma seçer.
  • Sınır ve uç durumları seçerek test eder.
  • Saf fonksiyonun özelliklerini tanır ve global kullanımına neden temkinli yaklaşıldığını açıklar.
Back to top