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ı
- 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()ilereturnfarkıreturnçalıştığında fonksiyonun sona ermesi- değer döndürmeyen fonksiyonun örtük
Nonedö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 * yukseklik2Aynı hesaplamayı tekrar etmek yerine ona bir ad verebiliriz:
def dikdortgen_alani(genislik, yukseklik):
return genislik * yukseklikArtı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.
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 * adetprint(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.
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.
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 + bBirincisi 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)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.
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.
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 sonucBuradaki 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:
500TL ve üzeri için0,- 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_fiyatolmalı. - İki parametre kullanılmalı.
- İndirim miktarı
fiyat * indirim_oraniilişkisiyle hesaplanmalı. - Sonuç
returnile 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))Tek sayfa özet
Bu hafta programı adlandırılmış ve yeniden kullanılabilir parçalara ayırmaya başladık.
- Fonksiyon
defile tanımlanır, adı ve parantezleriyle çağrılır. - Bir çağrı yürütüldüğü anda fonksiyon adı tanımlı değilse
NameErroroluş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
TypeErroroluşabilir. print()ekrana çıktı verir;returnfonksiyonun sonucunu çağıran koda gönderir.- Hiçbir değer döndürmeyen fonksiyon örtük olarak
Nonedö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
TypeErrorbeklendiğini bilir. print()ilereturnarasındaki farkı gösterir.returnçalıştığında fonksiyonun sona erdiğini belirler.- Değer döndürmeyen bir fonksiyonun
Nonedö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.