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 sonucu kime verdiğine de bakacağız.
Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.
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. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.
Kasadaki indirim kartını nasıl yazardınız?
Bir mağazada kasiyerler indirimli fiyatı her seferinde elle hesaplıyor ve arada bir yanlış yapıyor. Müdür bu hesabı bir kez yazıp bir karta koymaya karar veriyor. Kartın üstünde bir ad ve iki boş kutu var: ürünün fiyatı ve indirim oranı. Kasiyer bir üründe kartı açacak, iki kutuyu dolduracak, kartın adımlarını izleyecek ve çıkan sonucu müşterinin fişine ekleyecek.
Kartı siz yazın. 1. bölümde gördüğümüz sözde kodla, Türkçe ve madde madde yazın: kartın adı ne, hangi kutuları var, adımları neler, sonuç kasiyere nasıl ulaşıyor? Sonra kartınızı beş durumla deneyin:
- Müdür kartı sabah yazdı, gün boyunca hiç müşteri gelmedi. Kartın adımları o gün kaç kez izlendi?
- Kasiyer kartı açtı, fiyat kutusuna
800yazdı, indirim oranı kutusunu boş bıraktı. Kartınız ne yapıyor? - Müşteri iki ürün aldı. Kasiyer kartı iki kez izleyip iki indirimli fiyatı toplayacak. Kartınızın son adımı “sonucu söyle” ise kasiyer neyi toplar?
- Kampanyada bazı ürünler hediye, fiyatları
0olmalı. Kartınıza bu kuralı ekleyin. Sonra800TL’lik,%25indirimli hediye bir ürünle kartı baştan sona izleyin: sonuç kaç çıkıyor? - Fişe indirimden sonra KDV de eklenecek. KDV adımlarını aynı karta mı yazarsınız, ayrı bir karta mı?
Python bilmeniz gerekmiyor: “Hediyeyse sonuç 0” da bir adım. Kartınızı bir kenara yazın. Bölümde her parçanın Python’da nasıl yazıldığını göreceğiz. Alıştırmalardan önce kartınıza döneceğiz.
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 = 7
yukseklik2 = 3
alan2 = genislik2 * yukseklik2Aynı hesabı tekrar yazmak yerine ona bir ad verip bir fonksiyon yapabiliriz. Sonra aynı hesabı farklı değerlerle çalıştırırız. Hücre hangi iki satırı yazar?
Burada fonksiyonun iki yararı var:
- Hesaba ne yaptığını anlatan bir ad verir.
- Aynı hesap, yeniden yazılmadan farklı değerlerle kullanılabilir.
def, return ve parantezin içindeki adları bölümün geri kalanında tek tek göreceğiz.
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")def satırının altındaki girintili satırlara fonksiyonun gövdesi denir. Bu kod fonksiyonu tanımlar, yani selamla adını bu gövdeye bağlar, ama gövdeyi henüz çalıştırmaz. Gövdeyi çalıştırmak için fonksiyonu adıyla ve parantezle çağırırız:
selamla()Aşağıdaki program kaç satır yazar, satırların sırası ne olur?
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
Bu kez çağrı tanımdan önce yazılmış. Hücre çalışır mı?
Python dosyayı yukarıdan aşağıya satır satır çalıştırır. İlk satıra geldiğinde def satırı henüz çalışmadığı için selamla adı tanımlı değildir. Python bu durumda NameError hatası verir. Çağrıyı tanımın altına taşıyıp yeniden deneyin.
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ğırabiliriz:
selamla("Deniz")Çağrıda parantezin içine yazılan "Deniz" ise argümandır: parametreye o çağrıda verilen değer. Açılıştaki kartta boş kutular parametredir, kasiyerin kutulara yazdığı değerler argümandır.
Tanım: def selamla(ad):
↑
parametre
Çağrı: selamla("Deniz")
↑
argüman
Bir fonksiyon birden fazla parametre alabilir. Argümanlar parametrelere sırayla eşlenir: ilk argüman ilk parametreye, ikinci argüman ikinci parametreye gider. Hücre hangi iki satırı yazar?
Argüman sayısı parametrelerle uyuşmalıdır
toplam_fiyat iki parametre bekliyor. Bu çağrıda yalnız bir argüman var. Hücre ne yazar?
Python eksik argümanı kendiliğinden tamamlamaz, boş kutuyu 0 da saymaz. Eksik ya da fazla argümanla yapılan çağrı TypeError hatası verir. Eksik argümanda mesajda “missing” (eksik) sözcüğü geçer. Fazla argümanda mesaj “takes 2 positional arguments but 3 were given” (iki argüman alır ama üç verildi) gibi bir cümledir. Hücredeki çağrıya üçüncü bir argüman ekleyip bu mesajı da gö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
Çoğu zaman fonksiyonun hesapladığı değeri ekrana yazmak yerine programın başka bir yerinde kullanmak isteriz. Hücre ne yazar?
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 sonuc değişkenine atanır.
Aşağıdaki iki fonksiyon aynı toplamı hesaplıyor. Biri sonucu ekrana yazıyor, öteki döndürüyor. Hücre ne yazar? Sonra dondurerek_topla yerine yazdirarak_topla yazıp yeniden çalıştırın.
dondurerek_topla(10, 20) çağrısı 30 döndürür. ara_toplam 30 olur, genel_toplam 35 olur ve 35 yazılır.
yazdirarak_topla ile önce 30 yazılır: bu, fonksiyonun içindeki print()’in işidir. Fonksiyonda return olmadığı için ara_toplam None olur ve None + 5 satırı TypeError verir. Ekranda görünen 30, programın geri kalanının eline geçmedi.
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 satırı çalışmadıysa fonksiyon yine de bir değer döndürür: None. Kodda yazılmadığı hâlde Python’ın kendiliğinden döndürdüğü bu değere örtük dönüş değeri denir. None, “bu fonksiyon işe yarar bir sonuç döndürmedi” demektir.
Hücre kaç satır yazar, ikinci satırda ne görünür?
İki satır yazılır: 80.0 ve 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 hesapta 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. Hücre ne yazar?
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 önce indirimi, sonra KDV’yi hesaplayalım. Hücre ne yazar?
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ısını 7. bölümde göreceğiz. Şimdilik şunu aklınızda tutun:
Fonksiyonun ara hesapları mümkün olduğunca fonksiyonun içinde kalmalı; dışarıya yalnızca gereken sonuç verilmelidir.
Soruya dönelim: indirim kartı Python’da
Bölümün başında kasadaki indirim kartını yazıp beş durumla denemiştiniz. Python’daki karşılıkları şöyle:
| Durum | Sözde kodda | Python’da | Dikkat |
|---|---|---|---|
| Kart yazıldı, müşteri gelmedi | “İNDİRİMLİ FİYAT kartı: …” ve altındaki adımlar | def indirimli_fiyat(fiyat, indirim_orani): ve girintili gövde |
def gövdeyi çalıştırmaz. Gövde yalnız indirimli_fiyat(800, 0.25) gibi bir çağrıda çalışır |
| Oran kutusu boş | “Kutular: fiyat, indirim oranı” | fiyat ve indirim_orani parametre, çağrıdaki 800 ve 0.25 argüman |
indirimli_fiyat(800) TypeError verir. Python boş kutuyu 0 saymaz |
| İki ürünü toplamak | “Sonucu kasiyere ver” | return fiyat - indirim, sonra indirimli_fiyat(800, 0.25) + indirimli_fiyat(200, 0.10) |
“Sonucu söyle” print() olur: sonuç ekranda görünür ama çağıran kodun eline None geçer ve toplama yapılamaz |
| Hediye ürün | “Hediyeyse 0 ver ve dur” | if hediye_mi: altında return 0 |
return fonksiyonu bitirir. “Dur” yazılmazsa, yani return 0 yerine sonuc = 0 yazılırsa sonraki adım sonucu 600 yapar |
| İndirimden sonra KDV | “KDV için ayrı kart” | indirimli_fiyat ve kdv_hesapla ayrı fonksiyonlar |
Her fonksiyonun tek işi olursa ikisi ayrı ayrı denenir, yanlış sonucun hangi kartta çıktığı bulunur |
Kartınızı tabloyla karşılaştırın. Kartın son adımı “sonucu söyle” ise kasiyer ancak duyduğunu yeniden yazarak toplayabilir. Python’da bu yeniden yazma yoktur: değer return ile verilmezse çağıran kodun eline geçmez. Hediye kuralını “Hediyeyse sonuç 0” diye yazıp altına hesabı eklediyseniz kartınız hediye ürüne 600 TL yazar. Kurala “ve dur” eklemek, Python’da return yazmaktır.
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
Fonksiyonu 650, 500 ve 499 ile çağırıp sonuçları yazdırın.
def kargo_ucreti(sepet_tutari):
if sepet_tutari >= 500:
return 0
return 39.90
print(kargo_ucreti(650))
print(kargo_ucreti(500))
print(kargo_ucreti(499))İlk return çalışırsa fonksiyon orada biter, bu yüzden else yazmak gerekmez. else ile yazılmış bir çözüm de doğrudur.
Üret: indirimli fiyat fonksiyonu
Bölümün başındaki indirim kartını Python’a çevirin: bir ürünün fiyatını ve indirim oranını alan, indirim sonrası fiyatı döndüren bir fonksiyon yazın.
Başlangıç:
pass, “burada şimdilik hiçbir şey yapma” diyen boş bir satırdır. Kodunuzu yazınca silin. 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))Sıra sizde: hediye ürün ve fiş toplamı
Fonksiyona üçüncü bir parametre ekleyin: hediye_mi. Ürün hediyeyse fonksiyon 0 döndürsün ve hesaba hiç geçmesin. Sonra fonksiyonu iki kez çağırıp müşterinin fişini toplayın: 800 TL’lik %25 indirimli ürün ile 200 TL’lik %10 indirimli ürün.
Beklenen çıktı:
| Satır | Beklenen |
|---|---|
indirimli_fiyat(800, 0.25, True) |
0 |
| Fiş toplamı (iki ürün de hediye değil) | 780.0 |
Gövdenin ilk satırı if hediye_mi: olsun ve altında return 0 dursun. Fiş toplamı için indirimli_fiyat(800, 0.25, False) + indirimli_fiyat(200, 0.10, False) yazabilirsiniz.
def indirimli_fiyat(fiyat, indirim_orani, hediye_mi):
if hediye_mi:
return 0
indirim = fiyat * indirim_orani
return fiyat - indirim
print(indirimli_fiyat(800, 0.25, True))
fis_toplami = indirimli_fiyat(800, 0.25, False) + indirimli_fiyat(200, 0.10, False)
print(fis_toplami)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 yazın. - 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
returnsatırı çalışmayan fonksiyon örtük olarak, yani kodda yazılmadanNonedö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ı, denemeyi ve hatayı bulmayı kolaylaştırır.
- Açılıştaki indirim kartı bir fonksiyondur: boş kutular parametre, kasiyerin yazdığı değerler argüman, kasiyerin eline verilen sonuç dönüş değeridir. “Hediyeyse 0 ver ve dur” kuralı
return 0satırıdı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ırt eder.
- Parametre ile argümanı ayırt eder ve sayıları uyuşmadığında oluşan
TypeErrorhatasını açıklar. print()ilereturnarasındaki farkı açıklar.returnçalıştığında fonksiyonun sona erdiğini belirler.- Değer döndürmeyen bir fonksiyonun
Nonedöndürdüğünü açıklar. - 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.