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 yazmaya başlarız. Bir hatayı birden fazla yerde düzeltmek gerekir, programın hangi parçasının hangi işi yaptığını anlamak da zorlaşır.
print() ve input() gibi Python’ın hazır fonksiyonlarını zaten kullanıyorsunuz. Fonksiyon, bir işi yapan ve adıyla çalıştırılan kod parçasıdır. Bu hafta kendi fonksiyonlarımızı yazıp problemi küçük, adlandırılmış parçalara ayıracağız. def söz dizimini öğrenirken bir fonksiyonun hangi bilgiyi aldığına, hangi işi yaptığına ve hangi sonucu verdiğine de bakacağı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?
İkisinde de sonuç ekranda aynı görünebilir, ama ekrana yazılan bir sonucu programın geri kalanı kullanamaz. Bölüm boyunca şu soruya döneceğiz: hesabı kim yapıyor, sonuç ekrana mı gidiyor, programın geri kalanına mı?
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)Burada fonksiyonun iki yararı var:
- Hesaba ne yaptığını anlatan bir ad verir.
- Aynı hesap, yeniden yazılmadan farklı değerlerle kullanılabilir.
Bir kod parçasını fonksiyona dönüştürüp dönüştürmeyeceğinize karar verirken yalnızca “bu kod tekrar ediyor mu?” diye sormayın. “Bu parçanın tek ve açık bir işi var mı?” diye de sorun.
Fonksiyon tanımı ve çağrısı
Bir fonksiyonun tanımı def ile başlar:
def selamla():
print("Merhaba")Bu kod fonksiyonu tanımlar, ama henüz çalıştırmaz. Fonksiyonu çalıştırmak için onu adıyla ve parantezle çağırırız:
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 (def satırının altındaki girintili satırlar) 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 satır satır çalıştırır. Bu yüzden bir fonksiyon çağrısına geldiğinde o fonksiyonun def bloğu daha önce çalışmış 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.Fonksiyonun dosyanın herhangi bir yerinde yazılı olması yetmez; program çağrı satırına geldiği anda ad tanımlı olmalıdır. Bu yüzden şimdilik fonksiyon tanımlarını onları kullanan ana koddan önce yazın. Bu düzen kodu okumayı da kolaylaştırır.
Parametre ve argüman
Fonksiyonun farklı değerlerle çalışabilmesi için tanımdaki parantezin içine bir ad yazarız. Bu ada parametre denir:
def selamla(ad):
print("Merhaba", ad)Burada ad, fonksiyon tanımındaki parametredir.
Fonksiyonu şöyle çağırırsak:
selamla("Deniz")Çağrıda parantezin içine yazılan "Deniz" ise argümandır: parametreye o çağrıda verilen değer.
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 hatası verir:
# toplam_fiyat(25)
# TypeError: toplam_fiyat() missing 1 required positional argument: 'adet'Üç argüman vermek de aynı hatayı verir. Hata mesajında “missing” (eksik) veya “takes … but … were given” (… argüman alır ama … verildi) gibi ifadeler görürseniz çağrıdaki argüman sayısını fonksiyon tanımıyla karşılaştırın.
Bir fonksiyon çağrısında TypeError gördüğünüzde önce şunu kontrol edin: Fonksiyon kaç parametre tanımlıyor, ben kaç argüman verdim?
return: fonksiyonun döndürdüğü değer
Fonksiyonun işi ekrana bir şey yazmakla sınırlı değildir. Çoğu zaman hesaplanan değeri programın başka bir yerinde kullanmak isteriz.
def kare(sayi):
return sayi * sayi
sonuc = kare(5)
print(sonuc)return, hesaplanan değeri fonksiyonun çağrıldığı yere geri gönderir. Bu değere fonksiyonun dönüş değeri denir. Burada kare(5) çağrısının dönüş değeri 25’tir ve sonuc değişkenine atanır.
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() değeri ekranda kullanıcıya gösterir. return ise değeri fonksiyonu çağıran koda geri verir; o kod değeri bir değişkende tutabilir ya da başka bir hesapta kullanabilir.
return yoksa ne döner? None
Python’da fonksiyonun sonuna gelindiğinde hiçbir return deger satırı çalışmadıysa fonksiyon yine de bir değer döndürür: None. Burada None, “bu fonksiyon işe yarar bir sonuç döndürmedi” demektir.
Ş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() yazdırır. İkinci satırdaki None ise fonksiyonun dönüş değeridir.
Bu yüzden ekrana yazan fonksiyonla değer döndüren fonksiyonu birbirinden ayırın:
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 yine karşılaşacağız: 10. haftadaki sözlüklerde get(), bulunmayan bir anahtar için None verebilir; 13. ve 14. haftalarda da bazı geçersiz dönüşümleri açıkça işaretlemek için None kullanacağız. Yani None “ekrana hiçbir şey yazılmadı” demek değildir; Python’da kendi başına bir değerdir.
Tahmin et: return sonrasında ne olur?
Aşağıdaki programın çıktısını ve fonksiyonun dönüş değerini tahmin edin:
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 notunun ortalamasını hesaplamalı, ama şu anda sonucu 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ğerini herhangi bir değer gibi kullanabilirsiniz:
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)Ara değerin bir anlamı varsa (buradaki kdv gibi) onu ayrı bir değişkende tutmak kodu çoğu zaman daha okunur yapar.
Alıştırma: alan fonksiyonunu yazın
Bir dikdörtgenin alanını hesaplayan dikdortgen_alani fonksiyonunu tamamlayın.
Beklenen sonuç 40’tı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
Fonksiyonlarla tekrarı azaltmanın yanında büyük bir problemi anlamlı alt problemlere de bölebiliriz.
Örneğin bir alışveriş tutarı için indirimi ve KDV’yi hesaplayalı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.
Sonuç yanlış çıkarsa sorunun hangi adımda olduğunu bulmak da kolaylaşır: indirim ve KDV fonksiyonlarını ayrı ayrı deneyebilirsiniz.
Yerel değişkenler
Fonksiyonun içinde oluşturulan bir değişken genellikle yalnızca o fonksiyonun içinde kullanılır. Böyle değişkenlere yerel değişken denir:
def cevre_hesapla(kenar):
sonuc = kenar * 4
return sonucFonksiyon dışındaki kodun buradaki sonuc değişkenine doğrudan erişmesi gerekmez. Dışarıya verilmesi gereken değer return ile açıkça döndürülür.
Bir adın programın hangi bölümünde geçerli olduğuna kapsam (scope) denir. Ayrıntılarını 7. haftada ele alacağız. Şimdilik şu ilke yeterlidir:
Fonksiyonun ara hesapları mümkün olduğunca fonksiyonun içinde kalmalı; dışarıya yalnızca gereken sonuç verilmelidir.
Değiştir: kargo hesabını fonksiyona dönüştürün
Aşağıdaki programda kargo ücreti doğrudan hesaplanıyor:
Bu hesabı şöyle bir fonksiyona dönüştürün:
def kargo_ucreti(sepet_tutari):
...Fonksiyon şu değerleri döndürmelidir:
500TL ve üzeri için0,- Daha düşük tutarlar için
39.90.
Ü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.0’dı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. - Çağrı satırı çalıştığında fonksiyon henüz tanımlanmamışsa
NameErroroluşur; bu yüzden tanımları onları kullanan koddan önce yazmak güvenlidir. - 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()değeri ekrana yazar;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 yazılmış küçük bir fonksiyonun işi çoğu zaman tek cümleyle anlatılabilir.
- Problemi küçük fonksiyonlara ayırmak kodu okumayı, test etmeyi ve hata ayıklamayı kolaylaştırır.
Bir sonraki bölümde fonksiyon tasarlarken “çalışıyor mu?” sorusunun yanında şunları da soracağız: Hangi girdileri kabul etmeli, hangi sonucu döndürmeli, bunu nasıl doğruları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şmadığında
TypeErroroluşacağını 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.