Fonksiyonlara giriş

İlk beş haftada programlarımız giderek büyüdü: değişkenler kullandık, koşullar kurduk ve döngüler yazdık. Kod büyüdükçe aynı işlemi tekrar tekrar yazmak, bir hatayı birden fazla yerde düzeltmek veya programın hangi parçasının hangi işi yaptığını anlamak zorlaşır.

Bu hafta fonksiyonları, bir problemi daha küçük ve adlandırılmış parçalara ayırmanın temel aracı olarak kullanacağız. Amaç yalnızca def sözdizimini öğrenmek değildir. Bir fonksiyonun hangi bilgiyi aldığı, hangi işi yaptığı ve hangi sonucu ürettiği üzerinde duracağız.

Bu bölümün kapsamı

NoteSınavda sorulur
  • fonksiyon tanımı ile çağrısının ayrımı
  • parametre ile argüman ayrımı ve argüman sayısı uyuşmazlığı
  • print() ile return farkı
  • return çalıştığında fonksiyonun sona ermesi
  • değer döndürmeyen fonksiyonun örtük None döndürmesi
  • dönüş değerini başka ifadelerde kullanma
  • problemi küçük fonksiyonlara ayırmanın gerekçesi
  • değişken sayıda argüman (*args, **kwargs)
  • fonksiyon içinde fonksiyon tanımlama

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

Fonksiyon ekrana mı yazar, değer mi döndürür?

Bu iki iş birbirine benzer ama aynı şey değildir. Ekrana yazılan bir sonuç programın geri kalanında kullanılamaz. Bölümün ayırt edici sorusu budur: hesabı kim yapıyor, sonucu kim taşıyor?

Fonksiyon neden kullanılır?

Bir dikdörtgenin alanını birkaç yerde hesapladığımızı düşünelim:

genislik1 = 4
yukseklik1 = 6
alan1 = genislik1 * yukseklik1

genislik2 = 8
yukseklik2 = 3
alan2 = genislik2 * yukseklik2

Aynı hesaplamayı tekrar etmek yerine ona bir ad verebiliriz:

def dikdortgen_alani(genislik, yukseklik):
    return genislik * yukseklik

Artık farklı değerlerle aynı işlemi çağırabiliriz:

alan1 = dikdortgen_alani(4, 6)
alan2 = dikdortgen_alani(8, 3)

print(alan1)
print(alan2)

Fonksiyon burada iki avantaj sağlar:

  • Hesabın ne yaptığına bir ad verir.
  • Aynı mantığı farklı verilerle yeniden kullanmamızı sağlar.
Tip

Bir kod parçasını fonksiyona dönüştürmek için yalnızca “bu kod tekrar ediyor mu?” diye sormayın. “Bu parçanın tek ve açık bir işi var mı?” sorusu da önemlidir.

Fonksiyon tanımı ve çağrısı

Bir fonksiyonun tanımı def ile başlar:

def selamla():
    print("Merhaba")

Bu kod fonksiyonu tanımlar, fakat henüz çalıştırmaz. Fonksiyonu çalıştırmak için çağırmamız gerekir:

selamla()

Aşağıdaki programın çıktısını çalıştırmadan önce tahmin edin:

Fonksiyon tanımı programın başında yer alsa da gövdesi yalnızca mesaj_yaz() çağrısı yapıldığında çalışır.

Çağrı yapılırken fonksiyon tanımlı olmalıdır

Python dosyayı yukarıdan aşağıya yürüttüğü için, bir fonksiyon çağrısına geldiğinde o fonksiyonun tanımı daha önce yürütülmüş olmalıdır:

# selamla()   # Bu satır burada çalıştırılırsa NameError oluşur.

def selamla():
    print("Merhaba")

selamla()      # Bu çağrı geçerlidir.

“Fonksiyonu dosyanın aşağısında yazdım” tek başına yeterli değildir; önemli olan programın çağrı satırına geldiği anda adın tanımlı olmasıdır. Bu nedenle başlangıçta fonksiyon tanımlarını, onları kullanan ana koddan önce yazmak okunabilir bir düzen sağlar.

Parametre ve argüman

Fonksiyonun farklı verilerle çalışabilmesi için parametre kullanabiliriz:

def selamla(ad):
    print("Merhaba", ad)

Burada ad, fonksiyon tanımındaki parametredir.

Fonksiyonu şöyle çağırırsak:

selamla("Deniz")

"Deniz" değeri ise çağrı sırasında verilen argümandır.

Bu ayrımı şöyle düşünebilirsiniz:

Tanım:  def selamla(ad):
                     ↑
                 parametre

Çağrı: selamla("Deniz")
               ↑
            argüman

Bir fonksiyon birden fazla parametre alabilir:

def toplam_fiyat(birim_fiyat, adet):
    return birim_fiyat * adet
print(toplam_fiyat(25, 4))
print(toplam_fiyat(12.5, 3))

Argüman sayısı parametrelerle uyuşmalıdır

Yukarıdaki fonksiyon iki zorunlu parametre bekler. Eksik veya fazla argümanla çağrılırsa Python TypeError üretir:

# toplam_fiyat(25)
# TypeError: toplam_fiyat() missing 1 required positional argument: 'adet'

Benzer biçimde üç argüman vermek de hatadır. Hata mesajındaki “missing” veya “takes … but … were given” gibi ifadeler, çağrıdaki argüman sayısını fonksiyon tanımıyla karşılaştırmanız gerektiğine işaret eder.

Tip

Bir fonksiyon çağrısında TypeError gördüğünüzde ilk kontrollerden biri şudur: Fonksiyon kaç parametre tanımlıyor, ben kaç argüman verdim?

return: fonksiyonun ürettiği değer

Fonksiyon yalnızca ekrana bir şey yazdırmak zorunda değildir. Çoğu zaman asıl istediğimiz, hesaplanan değeri programın başka bir yerinde kullanmaktır.

def kare(sayi):
    return sayi * sayi

sonuc = kare(5)
print(sonuc)

return, fonksiyonun çağrıldığı yere bir değer gönderir.

Fonksiyon tanımı, çağrı, parametre, argüman ve dönüş değeri arasındaki ilişki.

Fonksiyon çağrı akışı

Aşağıdaki iki fonksiyon görünüşte benzer olsa da aynı işi yapmaz:

def yazdirarak_topla(a, b):
    print(a + b)

def dondurerek_topla(a, b):
    return a + b

Birincisi sonucu yalnızca ekrana yazar. İkincisinin sonucunu başka bir hesapta kullanabiliriz:

ara_toplam = dondurerek_topla(10, 20)
genel_toplam = ara_toplam + 5
print(genel_toplam)
Important

print() kullanıcıya çıktı gösterir. return ise fonksiyondan çağıran koda bir değer verir. Bunlar aynı şey değildir.

return yoksa ne döner? None

Python’da bir fonksiyonun sonuna ulaşılır ve hiçbir return deger çalışmazsa fonksiyon yine de bir değer döndürür: None. None, burada “bu fonksiyon kullanılabilir bir sonuç değeri döndürmedi” anlamına gelir.

Şu örneği inceleyin:

def ortalama_yazdir(a, b):
    print((a + b) / 2)

sonuc = ortalama_yazdir(70, 90)
print("Dönen değer:", sonuc)

Çıktı:

80.0
Dönen değer: None

İlk satırı fonksiyonun içindeki print() üretir. İkinci satırdaki None ise fonksiyonun dönüş değeridir.

Bu nedenle şu iki amaç ayrılmalıdır:

def ortalama_yazdir(a, b):
    print((a + b) / 2)

def ortalama_hesapla(a, b):
    return (a + b) / 2

İkinci fonksiyonun sonucu başka bir hesaplamada güvenle kullanılabilir.

Note

None ile ilerleyen haftalarda tekrar karşılaşacağız: sözlükte get() eksik anahtarda None verebilir; 13. ve 14. haftalarda da bazı geçersiz dönüşümleri açıkça işaretlemek için None kullanacağız. Bu nedenle None, yalnızca “ekrana hiçbir şey yazılmadı” anlamına gelmez; Python’da gerçek bir özel değerdir.

print ile ekrana yazdırma ve return ile çağırana değer döndürme arasındaki fark.

print ve return farkı

Tahmin et — return sonrasında ne olur?

Aşağıdaki fonksiyonun çıktısını ve dönüş değerini düşünün:

return çalıştığında fonksiyon sona erer. Bu nedenle return satırından sonraki print() çağrısına ulaşılmaz.

Alıştırma — eksik dönüş değerini tamamlayın

Aşağıdaki fonksiyon iki sınavın basit ortalamasını hesaplamalıdır; ancak şu anda sonuç döndürmüyor.

Fonksiyonun 80.0 değerini döndürmesini sağlayın.

sonuc değişkeni zaten hesaplandı. Fonksiyonun sonuna return sonuc ekleyebilirsiniz.

def ortalama(not1, not2):
    sonuc = (not1 + not2) / 2
    return sonuc

print(ortalama(70, 90))

Fonksiyonun sonucunu başka bir ifadede kullanmak

Dönüş değeri normal bir değer gibi kullanılabilir:

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

urun_tutari = 500
kdv = kdv_hesapla(urun_tutari, 0.20)
odenecek = urun_tutari + kdv

print(odenecek)

Fonksiyon çağrısı doğrudan bir ifadenin içinde de yer alabilir:

odenecek = urun_tutari + kdv_hesapla(urun_tutari, 0.20)

Başlangıç düzeyinde, ara değer anlamlıysa ayrı bir değişkende tutmak çoğu zaman kodu daha okunur yapar.

Alıştırma — alan fonksiyonunu yazın

Bir dikdörtgenin alanını hesaplayan dikdortgen_alani fonksiyonunu tamamlayın.

Beklenen sonuç 40tır.

Fonksiyonun dönüş değeri genislik * yukseklik olmalıdır.

def dikdortgen_alani(genislik, yukseklik):
    return genislik * yukseklik

print(dikdortgen_alani(8, 5))

Bir problemi küçük fonksiyonlara ayırmak

Fonksiyon kullanmanın asıl gücü yalnızca tekrar azaltmak değildir. Büyük bir problemi anlamlı alt problemlere ayırabiliriz.

Örneğin bir alışveriş tutarında indirim ve KDV hesabı yapalım:

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

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

urunler_toplami = 1000
indirim = indirim_hesapla(urunler_toplami, 0.10)
indirimli_tutar = urunler_toplami - indirim
kdv = kdv_hesapla(indirimli_tutar, 0.20)
odenecek = indirimli_tutar + kdv

print(odenecek)

Burada her fonksiyonun işi tek cümleyle açıklanabilir:

  • indirim_hesapla: verilen tutarın indirim miktarını bulur.
  • kdv_hesapla: verilen tutarın KDV miktarını bulur.

Bu yapı, hesaplamanın hangi adımında sorun olduğunu bulmayı da kolaylaştırır.

Yerel değişken fikri

Fonksiyon içinde oluşturulan bir değişken çoğunlukla o fonksiyonun kendi çalışma alanına aittir:

def cevre_hesapla(kenar):
    sonuc = kenar * 4
    return sonuc

Buradaki sonuc değişkenini fonksiyon dışındaki kodun doğrudan kullanmasına ihtiyaç yoktur. Fonksiyon dışına taşınması gereken bilgi return ile açıkça verilir.

Kapsam (scope) konusunun ayrıntılarını 7. haftada ele alacağız. Şimdilik şu ilke yeterlidir:

Bir fonksiyonun iç hesabı, mümkün olduğunca fonksiyonun içinde kalmalı; dışarıya yalnızca ihtiyaç duyulan sonuç verilmelidir.

Değiştir — kargo hesabını fonksiyona dönüştürün

Aşağıdaki programda kargo ücreti doğrudan hesaplanıyor:

Bunu şu tasarıma dönüştürün:

def kargo_ucreti(sepet_tutari):
    ...

Fonksiyon:

  • 500 TL ve üzeri için 0,
  • Daha düşük tutarlar için 39.90

döndürmelidir.

Üret — indirimli fiyat fonksiyonu

Bir ürünün fiyatını ve indirim oranını alan, indirim sonrası fiyatı döndüren bir fonksiyon yazın.

Başlangıç:

Beklenen sonuç 600.0dır.

Kabul kriterleri:

  • Fonksiyonun adı indirimli_fiyat olmalı.
  • İki parametre kullanılmalı.
  • İndirim miktarı fiyat * indirim_orani ilişkisiyle hesaplanmalı.
  • Sonuç return ile döndürülmeli.
  • Fonksiyon farklı fiyat ve oranlarla da çalışabilmeli.

Önce indirim = fiyat * indirim_orani hesaplanabilir. Sonra fiyat - indirim döndürülebilir.

def indirimli_fiyat(fiyat, indirim_orani):
    indirim = fiyat * indirim_orani
    return fiyat - indirim

print(indirimli_fiyat(800, 0.25))

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. Fonksiyon tanımı ile fonksiyon çağrısı arasındaki fark nedir?

  • A. Tanım fonksiyonu bir kez çalıştırır, çağrı ise tekrar çalıştırır
  • B. Tanım fonksiyonun ne yapacağını def ile belirler ama çalıştırmaz; çağrı fonksiyonu o anda çalıştırır
  • C. Tanım parantezli, çağrı parantezsiz yazılır
  • D. İkisi aynı şeydir; def yazımı isteğe bağlıdır
  • E. Tanım dosyanın sonunda, çağrı başında yer alır

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

print(selamla())

def selamla():
    return "Merhaba"
  • A. Bir fonksiyon print() içinde çağrılamaz
  • B. Parametresiz fonksiyon çağrılamaz
  • C. Çağrı çalıştığı anda selamla adı henüz tanımlı değildir; NameError oluşur
  • D. Fonksiyon adı bir fiil olduğu için
  • E. return yerine print() kullanılmalıydı

Madde 3. def toplam(a, b): tanımındaki a ve b ile toplam(4, 7) çağrısındaki 4 ve 7 sırasıyla nedir?

  • A. Dördü de argümandır
  • B. Dördü de parametredir
  • C. a ve b yerel değişken değildir; yalnızca birer etikettir
  • D. a ve b parametredir; 4 ve 7 argümandır
  • E. a ve b argümandır; 4 ve 7 parametredir

Madde 4. Aşağıdaki çağrı hangi hatayı verir?

def alan(en, boy):
    return en * boy

print(alan(5))
  • A. Hata vermez; eksik parametre 0 sayılır
  • B. NameError; boy tanımsızdır
  • C. Hata vermez; 5 ikisine birden atanır
  • D. ValueError; 5 geçersiz bir değerdir
  • E. TypeError; iki zorunlu parametre varken tek argüman verilmiştir

Madde 5. print() ile return arasındaki temel fark nedir?

  • A. print() değeri ekranda gösterir; return değeri çağıran koda geri verir ve o değer sonradan kullanılabilir
  • B. return kullanılan bir fonksiyon ekrana hiçbir şey yazamaz
  • C. İkisi de aynı işi yapar; return yalnızca fonksiyon içinde yazılır
  • D. print() yalnızca metin, return yalnızca sayı taşıyabilir
  • E. return ekrana yazar, print() değeri saklar

Madde 6. Aşağıdaki program neden Toplam: None yazdırır?

def topla(a, b):
    print(a + b)

sonuc = topla(2, 3)
print("Toplam:", sonuc)
  • A. İki argüman verildiği için fonksiyon iki değer döndürmeye çalışıyor
  • B. Fonksiyon sonucu ekrana yazıyor ama geri döndürmüyor; return olmadığı için sonuc None olur
  • C. print() çağrısı toplamı bozduğu için
  • D. sonuc değişkeni yanlış adlandırıldığı için
  • E. a + b işlemi fonksiyon içinde yapılamaz

Madde 7. Aşağıdaki program ne yazdırır?

def kontrol(puan):
    if puan >= 50:
        return "Geçti"
    return "Kaldı"
    print("Bu satır")

print(kontrol(80))
  • A. Kaldı
  • B. Geçti ve Kaldı
  • C. Geçti
  • D. None
  • E. Geçti ve Bu satır

Madde 8. İçinde hiç return bulunmayan bir fonksiyonun dönüş değeri nedir?

  • A. Fonksiyonun son satırındaki değer
  • B. Boş metin ("")
  • C. False
  • D. None
  • E. 0

Madde 9. Aşağıdaki program ne yazdırır?

def iki_kat(x):
    return x * 2

a = iki_kat(3)
b = iki_kat(a)
print(b)
  • A. 24
  • B. None
  • C. 6
  • D. 3
  • E. 12

Madde 10. Bir fonksiyon yalnızca kod tekrarını azaltmak için değil, başka hangi nedenle de yararlıdır?

  • A. Problemi ayrı ayrı okunabilen, denenebilen ve düzeltilebilen parçalara böler
  • B. Programın bellek kullanımını her zaman azaltır
  • C. Değişkenlerin tür dönüşümünü kendiliğinden yapar
  • D. Söz dizimi hatalarını engeller
  • E. Kodun satır sayısını mutlaka düşürür

Madde 1 — Doğru: B. def satırı yalnızca adı ve gövdeyi kaydeder. Gövde ancak ad parantezle çağrıldığında yürütülür.

  • A yanlış: Tanım hiç çalıştırmaz; ilk yürütme ilk çağrıda olur.
  • C yanlış: İkisinde de parantez vardır; tanımda parametreler, çağrıda argümanlar bulunur.
  • D yanlış: def olmadan fonksiyon oluşmaz; ikisi ayrı adımlardır.
  • E yanlış: Tam tersi güvenlidir: ad kullanılmadan önce tanımlı olmalıdır.

Madde 2 — Doğru: C. Python satırları sırayla yürütür. def satırına gelinmeden ad var olmaz.

  • A yanlış: Çağrılabilir; dönüş değeri doğrudan print()e verilir.
  • B yanlış: Parametresiz fonksiyonlar boş parantezle çağrılır ve bu geçerlidir.
  • D yanlış: Ad seçimi hata üretmez; fiil kullanmak yaygın ve önerilen bir adlandırmadır.
  • E yanlış: return doğru kullanılmış; sorun çağrının tanımdan önce gelmesi.

Madde 3 — Doğru: D. Parametre tanımdaki addır; argüman çağrıda o ada verilen değerdir.

  • A yanlış: Tanımdaki adlar parametredir.
  • B yanlış: Çağrıda verilen değerler argümandır.
  • C yanlış: Parametreler fonksiyon çağrıldığında oluşan yerel değişkenlerdir.
  • E yanlış: Terimler ters yazılmış.

Madde 4 — Doğru: E. Python eksik argümanı kendiliğinden tamamlamaz; çağrı kurulurken hata verir.

  • A yanlış: Böyle bir varsayılan yoktur; varsayılan istenirse tanımda yazılır.
  • B yanlış: boy tanımda yer alan bir parametredir; sorun değerinin verilmemesi.
  • C yanlış: Bir argüman yalnızca bir parametreye karşılık gelir.
  • D yanlış: 5 geçerli bir sayıdır; sorun argüman sayısında.

Madde 5 — Doğru: A. Ekranda görünen bir değer programın geri kalanında kullanılamaz. Hesaplamayı taşıyan returndur.

  • B yanlış: Aynı fonksiyon hem yazdırıp hem değer döndürebilir; ayrı işlerdir.
  • C yanlış: Aynı işi yapmazlar; biri gösterir, diğeri değeri taşır.
  • D yanlış: İkisi de her türle çalışır.
  • E yanlış: Görevler ters yazılmış.

Madde 6 — Doğru: B. Ekrandaki 5 fonksiyonun kendi çıktısıdır. Çağıran tarafa taşınan değer return ile verilir.

  • A yanlış: Argüman sayısıyla dönüş değeri arasında böyle bir ilişki yoktur.
  • C yanlış: print() değeri değiştirmez; yalnızca gösterir.
  • D yanlış: Ad seçimi değeri etkilemez.
  • E yanlış: Yapılabilir ve zaten yapılıyor; ekranda 5 görünüyor.

Madde 7 — Doğru: C. return çalıştığı anda fonksiyon biter; sonrasındaki hiçbir satıra ulaşılmaz.

  • A yanlış: 80 >= 50 doğrudur; ilk return çalışır.
  • B yanlış: İlk return fonksiyonu sonlandırdığı için ikincisine sıra gelmez.
  • D yanlış: Fonksiyon açık bir değer döndürüyor.
  • E yanlış: return sonrası satırlar erişilemez; hiç çalışmazlar.

Madde 8 — Doğru: D. Python açıkça bir değer döndürülmediğinde örtük olarak None döndürür.

  • A yanlış: Son ifadenin değeri kendiliğinden dönmez; bu bazı başka dillerin davranışıdır.
  • B yanlış: Metin varsayılanı da yoktur.
  • C yanlış: False bir bool değeridir ve varsayılan olarak döndürülmez.
  • E yanlış: Sayısal bir varsayılan yoktur.

Madde 9 — Doğru: E. a 6 olur, sonra 6nın iki katı olan 12 bye atanır.

  • A yanlış: Yalnızca iki çağrı var; değer iki kez katlanır, üç kez değil.
  • B yanlış: Fonksiyon açıkça bir değer döndürüyor.
  • C yanlış: Bu a değeridir; b ikinci çağrının sonucudur.
  • D yanlış: İlk çağrı değeri zaten ikiye katlıyor.

Madde 10 — Doğru: A. Küçük bir fonksiyonun doğruluğu tek başına sınanabilir; hata da tek bir yerde aranır.

  • B yanlış: Fonksiyon çağrısı kendi başına bellek kazandırmaz.
  • C yanlış: Dönüşüm yine açıkça yazılır.
  • D yanlış: Sözdizimi hataları fonksiyon içinde de çıkar.
  • E yanlış: Tanım satırlarıyla birlikte toplam uzunluk artabilir; kazanç okunabilirliktedir.

Tek sayfa özet

Bu hafta programı adlandırılmış ve yeniden kullanılabilir parçalara ayırmaya başladık.

  • Fonksiyon def ile tanımlanır, adı ve parantezleriyle çağrılır.
  • Bir çağrı yürütüldüğü anda fonksiyon adı tanımlı değilse NameError oluşur; başlangıçta tanımları kullanımdan önce yazmak güvenli bir düzendir.
  • Parametre fonksiyon tanımındaki addır; argüman çağrıda verilen değerdir.
  • Zorunlu parametre sayısı ile verilen argüman sayısı uyuşmazsa TypeError oluşabilir.
  • print() ekrana çıktı verir; return fonksiyonun sonucunu çağıran koda gönderir.
  • Hiçbir değer döndürmeyen fonksiyon örtük olarak None döndürür.
  • return çalıştığında fonksiyon sona erer.
  • Fonksiyonun dönüş değeri başka değişkenlerde ve ifadelerde kullanılabilir.
  • İyi bir başlangıç fonksiyonunun işi çoğu zaman tek cümleyle açıklanabilir.
  • Problemi küçük fonksiyonlara ayırmak kodu okumayı, test etmeyi ve hata ayıklamayı kolaylaştırır.

Bir sonraki bölümde fonksiyonları yalnızca “çalışıyor mu?” diye değil, hangi girdileri kabul etmeli, hangi sonucu üretmeli ve bunu nasıl doğrulamalıyız? sorularıyla tasarlayacağız.

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

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

  • Fonksiyon tanımı ile fonksiyon çağrısını birbirinden ayırır.
  • Parametre ile argümanı ayırır ve sayıları uyuşmazsa TypeError beklendiğini bilir.
  • print() ile return arasındaki farkı gösterir.
  • return çalıştığında fonksiyonun sona erdiğini belirler.
  • Değer döndürmeyen bir fonksiyonun None döndürdüğünü bilir.
  • Fonksiyonun dönüş değerini başka ifadelerde ve çağrılarda kullanır.
  • Problemi küçük fonksiyonlara ayırmanın gerekçesini açıklar.
Back to top