Kodun yapı taşları: tanımlama, kapsam, parametreler
Bir problemi çözerken ilk akla gelen yöntem, adımları sırasıyla tekrarlı biçimde yazmaktır. Fonksiyonlar, aynı işi yapan kod parçasına bir ad verip onu istediğimiz yerde çağırmamızı sağlar.
Yarıçapı 5 ve 8 olan iki dairenin alanını hesaplayan kodu yazmamız istendiğinde, önce birini sonra diğerini yazabiliriz:
Bu kod çalışır, ancak programlarımız büyüdükçe pratik bir yöntem olmaktan çıkar: formülü değiştirmek gerekirse her kopyayı ayrı ayrı düzeltmek gerekir.
Aynı hesabı bir kez tanımlayıp iki kez çağırıyoruz:
Fonksiyonlar kodu tekrar kullanılabilir, okunabilir ve yönetilebilir hale getirir: kod tekrarını azaltır, okunabilirliği artırır, bakımı kolaylaştırır.
Python’da bir fonksiyon tanımlamak için def anahtar kelimesini kullanırız.
Parantez içindeki adlar parametre, çağrıda verilen değerler argümandır. İlk satırdaki üç tırnaklı metin (docstring) fonksiyonun ne yaptığını açıklar.
Bir fonksiyonu çağırmak için adını ve parantez içinde argümanlarını yazarız. return ile dönen değer bir değişkene atanabilir.
return)return ifadesi ile bir değer döndürebilir; return çalıştığı anda fonksiyon biter.return kullanılmazsa, fonksiyon varsayılan olarak None değerini döndürür.Modern Python’da kodun okunabilirliğini artırmak ve hataları önlemek için tip ipuçları kullanılır. Bu, fonksiyonun hangi tipte parametreler beklediğini ve ne döndüreceğini belirtir.
Bu ipuçları zorunlu değildir, Python kodu yine çalıştırır; ama çok iyi bir pratiktir.
mypy ile Kontrolmypy, kodunuzu çalıştırmadan önce tip ipuçlarını kontrol eden bir araçtır. Amacı, olası TypeError gibi hataları en erken aşamada yakalamaktır.
Fonksiyon tanımının hemen altındaki üç tırnaklı metin docstring’dir. İyi bir docstring fonksiyonun ne beklediğini, ne döndürdüğünü, varsa neyi değiştirdiğini ve hangi durumda hata verdiğini söyler: çağıranla fonksiyon arasındaki sözleşme.
help() bu metni ekrana getirir; editörler de fare üzerine gelince gösterir. Buradaki raise, hatayı çağırana bildirir — ayrıntısı Bölüm 09’da.
Bir fonksiyon hem veriyi alır, hem doğrular, hem hesaplar, hem ekrana yazarsa onu test etmek ve değiştirmek zorlaşır. Her fonksiyonun tek bir işi olsun; adı o işi söylesin.
Sorumluluklar ayrılınca her parça tek başına çağrılabilir ve sınanabilir:
Girdi alma ve ekrana yazma kenarda, hesap ortada durur; ortalama([70, 90]) için input() beklemek gerekmez.
Parametre olarak verilen 3 değerden en büyük olanı bulan fonksiyonu yazalım.
Bir değişkenin kapsamı (scope), o değişkenin programın hangi bölümlerinde erişilebilir olduğunu belirler.
xFonksiyon içindeki x = 5 satırı yeni bir yerel x oluşturur; dışarıdaki global x bundan etkilenmez.
Aynı nedenle, fonksiyon içinden global x’i değiştirmeye çalıştığımızda beklediğimiz olmaz:
Fonksiyon içindeki x = 20 satırı global x’i değiştirmedi; yeni bir yerel x oluşturdu.
UnboundLocalErrorPeki fonksiyon dıştaki değeri önce okuyup sonra değiştirmek isterse?
Python bir adın yerel olup olmadığına satır satır değil, fonksiyon gövdesinin tamamına bakarak karar verir. - Gövdede sayac’a atama olduğu için sayac baştan yerel sayılır; print satırı çalıştığında bu yerel ada henüz değer verilmemiştir → UnboundLocalError. - Dıştaki sayac’ı gerçekten değiştirmek istiyorsak bunu açıkça söylememiz gerekir.
global Bildirimiglobal ile bildirmemiz gerekir.nonlocalnonlocal: İç içe fonksiyonlarda, bir üstteki (ama global olmayan) fonksiyonun değişkenini değiştirmek için kullanılır.ic_fonksiyon, nonlocal a sayesinde dış fonksiyonun a değişkenini okur ve değiştirir:
Fonksiyonlarda parametrelere varsayılan değerler atanabilir. Eğer fonksiyon çağrılırken bu parametreye bir değer verilmezse, varsayılan değer kullanılır.
Varsayılan değer, parametreyi isteğe bağlı yapar:
Argümanlar iki biçimde verilebilir: konumsal (sırayla) ya da anahtar kelimeli (parametrenin adıyla). Anahtar kelimeli çağrıda sıra önemli değildir ve hangi değerin hangi parametreye gittiği çağrı satırından okunur.
Birden fazla isteğe bağlı parametre olduğunda anahtar kelime kullanın; kaydet(dosya, True, False) yerine kaydet(dosya, uzerine_yaz=True, yedekle=False) kendini açıklar. Konumsal argümanlar her zaman anahtar kelimelilerden önce gelmelidir.
Her müşteri için yeni bir sepet açan bir fonksiyon yazalım: sepet verilmezse boş bir sepetle başlasın, ilk ürünü koysun. Ali kalem, Ayşe defter alıyor. Ali’nin sepetinde ne var?
Varsayılan [] her çağrıda yeniden oluşturulmaz: - Python onu def satırı çalışırken bir kez oluşturur ve sonraki çağrılar aynı listeyi paylaşır. - Ali ile Ayşe aynı sepeti kullanıyor.
Liste gibi değiştirilebilir (mutable) tipler — oluşturulduktan sonra içeriği değişebilen nesneler — bu yüzden varsayılan değer olarak kullanılmamalıdır.
None KalıbıVarsayılan değer olarak None kullanın ve listeyi gövdede oluşturun; her çağrı kendi sepetini alır. Buradaki None, “çağıran sepet vermedi” anlamına gelen bir işarettir (nöbetçi değer).
İki sayıyı toplayan bir fonksiyon kolay:
Üç sayı için yenisi gerekir:
Peki 100 sayı? Bu yöntem sürdürülebilir değil. Çözüm, fonksiyonun “ne kadar parametre gelirse” almasını sağlamaktır. İşte burada * ve ** operatörleri devreye girer.
*args, **kwargs)* ve ** sembolleri, Python’da “paketleme” (packing) ve “açma” (unpacking) işlemleri için kullanılan özel operatörlerdir.
* : Bir dizi veya demet (list, tuple) gibi konumsal elemanları paketler veya açar.** : Bir sözlük (dict) gibi anahtar-değer çiftlerini paketler veya açar.*args: Gelenleri Bir Demete (Tuple) Paketle*args sözdizimi, fonksiyona gönderilen tüm konumsal argümanları toplar ve onları tek bir tuple içine paketler.
**kwargs: Gelenleri Bir Sözlüğe (Dict) Paketle**kwargs sözdizimi, fonksiyona gönderilen tüm anahtar kelimeli argümanları (isim="Ali" gibi) toplar ve tek bir sözlük içine paketler.
Bölüm 03’ü hatırlayın
Çıktıdaki Isim noktasız I ile başladı: capitalize() de upper() gibi Türkçe i → İ kuralını bilmez.
args (“arguments”ın kısaltması) ve kwargs (“keyword arguments”ın kısaltması) isimleri zorunlu değildir, sadece Python topluluğunda yaygın olarak kullanılan bir gelenektir (convention). Önemli olan * ve ** sembolleridir.
Tek satırda tanımlanan, isimsiz (anonim) fonksiyonlardır. Basit işlemler için kullanılırlar.
Sözdizimi: lambda argümanlar: ifade
Asıl güçleri, sorted, map, filter gibi üst düzey fonksiyonlara argüman olarak verildiklerinde ortaya çıkar.
def kullanın.def ile tanımlayın.Kendini çağıran fonksiyonlardır. Bir problemi daha küçük, kendi benzeri alt problemlere bölerek çözerler.
Özyineleme Uyarısı
RecursionError alabilirsiniz.Boşlukları doldurup çalıştırın; sonra çözümle karşılaştırın.
Bir sayının asal olup olmadığını kontrol eden asal_mi(sayi: int) -> bool fonksiyonunu tamamlayın. 1 asal değildir. 7 için True, 10 için False çıkmalı.
Çözüm
False — 1 ve altındaki sayılar asal sayılmaz; fonksiyon burada hemen biter.int(sayi ** 0.5) + 1 — bölen ararken kareköke kadar bakmak yeterlidir. Karekökten büyük bir bölen varsa, karekökten küçük bir eşi de vardır ve onu zaten denemiş oluruz. range bitişi dışarıda bıraktığı için + 1 ekliyoruz.sayi % i == 0 — kalan sıfırsa i bir bölendir; False döndürüp duruyoruz.True buradan gelir.Verilen sayıların en büyüğünü bulan en_buyugu_bul(*sayilar) fonksiyonunu tamamlayın. Hiç argüman gelmezse None döndürmelidir.
Çözüm
*sayilar: float — çağrıda verilen tüm sayılar bir demette toplanır. Hiç sayı verilmemişse demet boştur; not sayilar bunu yakalar ve None döneriz.sayi > en_buyuk — ilk sayıyı geçici olarak en büyük kabul edip kalanları onunla karşılaştırıyoruz; daha büyüğünü gördükçe güncelliyoruz. Başlangıcı 0 yapmak yaygın bir hatadır: -1, -5, -2 için sonuç yanlış çıkardı.filter ve lambdaBir listedeki çift sayıları lambda ve filter() kullanarak süzün. filter(fonksiyon, liste), listedeki her elemanı fonksiyona verir ve True dönenleri tutar.
Çözüm
x % 2 == 0 — “bu sayı çift mi?” sorusunun tek satırlık karşılığı. filter, listedeki her elemanı bu fonksiyona gönderir ve yalnızca True dönenleri tutar.filter sonucu doğrudan bir liste olarak vermez; ekrana yazdırmak için list() ile listeye çeviriyoruz.profil_olustur fonksiyonu zorunlu olarak isim ve soyisim almalı, isteğe bağlı olarak **kwargs ile ek bilgiler (yaş, şehir vb.) alabilmelidir.
Çözüm
**ek_bilgiler — adıyla verilen fazladan argümanlar (yas=30, sehir="Ankara") bu sözlükte toplanır; zorunlu isim ve soyisim her zamanki gibi alınır.ek_bilgiler.items() — sözlüğü anahtar-değer çiftleri hâlinde dolaşmanın yolu; her çift bir satır olarak yazdırılır.meslek) gelmesi sorun çıkarmaz: fonksiyon hangi ek bilgilerin geleceğini önceden bilmek zorunda değildir.Parametre olarak aldığı metni, döngü kullanmadan, özyineli olarak tersine çeviren ters_cevir fonksiyonunu tamamlayın. İpucu: ters_cevir("abc") = ters_cevir("bc") + "a"
Çözüm
metin — boş ya da tek karakterli bir metnin tersi kendisidir; özyinelemenin durduğu temel durum budur.ters_cevir(metin[1:]) + metin[0] — metni ikiye ayırıyoruz: ilk karakter ve kalanı. Kalanın tersini fonksiyonun kendisinden isteyip ilk karakteri sona ekliyoruz.Her soruda önce kendi cevabınızı seçin, sonra cevap anahtarındaki gerekçeleri okuyun; yanlış şıklar bu konuda gerçekten yapılan hatalardan seçildi.
Merhaba Ada ve Merhaba AdaMerhaba Ada ve NoneMerhaba AdaNone ve Merhaba Ada2010UnboundLocalErrorNone30['kalem']['kalem', 'defter']['defter'][]Aşağıdakilerden hangisi *sayilar için doğrudur?
args olmalıdırAliVeliAyşe80("Ali", 80)10 yazar, sayac 11 olur11 yazarUnboundLocalError; atama yüzünden sayac yerel sayılır10 yazar; sayac dıştan okunur, sonra yerel olurNameError; sayac tanımlı değildira.txt False Falsea.txt True Falsea.txt False Truea.txt None FalseParametre olarak aldığı metnin sesli harf sayısını döndüren sesli_say(metin: str) -> int fonksiyonunu yazın. Büyük/küçük harf fark etmesin ("aeıioöuüAEIİOÖUÜ").
def ile kod blokları oluşturma ve return ile değer döndürme; return yoksa None.sayi: int -> bool gibi ipuçları ve mypy ile erken hata yakalama.help() gösterir. Her fonksiyonun tek işi olsun.UnboundLocalError); global ve nonlocal yalnızca yeniden atama için gerekir.*args, **kwargs.None kalıbı kullanılır.lambda ile anonim fonksiyonlar; temel durum + özyineli adım ile özyineleme.def/return ile fonksiyon tanımlar; return olmayan fonksiyonun None döndürdüğünü bilir.help() ile okur; tek işi olan fonksiyonlar tasarlar.UnboundLocalError’ın nedenini ve global/nonlocal’ın yalnız yeniden atama için gerektiğini açıklar.None kalıbıyla önler.*args/**kwargs alan fonksiyon yazar ve çağırır.lambda’yı sorted/filter ile kullanır; ne zaman def tercih edileceğini söyler.İleri Programlama – OMÜ | 🏠