Fonksiyonlar, kapsam ve modüller

Nesne tabanlı programlama, iyi fonksiyon tasarımının yerine geçmez. Aksine sınıf metotları da fonksiyon mantığı üzerine kurulur. Bu hafta Python’da parametre, dönüş değeri, kapsam ve modül kullanımını hızlı biçimde toparlayacağız; aynı zamanda “tek büyük program” yerine sorumlulukları küçük parçalara ayırma alışkanlığını güçlendireceğiz.

Bu hafta neleri yapabilmelisiniz?

  • def ile fonksiyon tanımlayabilmeli,
  • parametre ile argüman ayrımını açıklayabilmeli,
  • return ile sonuç döndürebilmeli,
  • yerel ve küresel kapsam farkını gözlemleyebilmeli,
  • fonksiyona değiştirilebilir nesne verildiğinde yan etkileri fark edebilmeli,
  • kendi modülünüzü oluşturup import edebilmeli,
  • bir programı anlamlı sorumluluklara göre fonksiyonlara ayırabilmelisiniz.

Fonksiyon: adlandırılmış davranış

first ve second parametrelerdir; çağrıdaki 70 ve 90 argümanlardır.

Fonksiyon adı, “nasıl yapılıyor?” ayrıntısından önce “ne yapıyor?” sorusuna cevap vermelidir. calculate_average bu açıdan process veya do_it gibi belirsiz adlardan daha açıklayıcıdır.

Yerel kapsam

tax fonksiyon içinde yereldir. rate dış kapsamdan okunmuştur. Küresel değişken okumak mümkün olsa da iş kurallarını büyük ölçüde küresel duruma bağlamak programı zor izlenir hâle getirir.

Değiştirilebilir parametre ve yan etki

Aşağıdaki örneği çalıştırmadan önce numbers listesinin değişip değişmeyeceğini tahmin edin.

Çağrı sırasında scores parametre adı, çağıranın verdiği aynı liste nesnesine bağlanır. Python listeyi otomatik olarak kopyalamaz. append() bu ortak liste nesnesinin durumunu değiştirdiği için çağıran taraf da yeni içeriği görür. Bu davranış bazen istenen şeydir; bazen de beklenmedik yan etkidir. Fonksiyonun sözleşmesi bunu açık kılmalıdır.

Note“Pass-by-reference” diye ezberlemeyin

Bu davranışı “Python her şeyi referansla geçirir” biçiminde genellemek yanıltıcı olabilir. Bu derste daha güvenli zihinsel model şudur: adlar nesnelere bağlanır; fonksiyon çağrısında parametre adları da verilen nesnelere bağlanır. Mutable nesne yerinde değiştirilirse bu değişiklik aynı nesneyi gören yerlerden fark edilir.

Alternatif olarak yeni liste döndürebiliriz:

Fonksiyonları sorumluluğa göre ayırmak

Tek bir fonksiyonun aynı anda veri alma, doğrulama, hesaplama, dosyaya yazma ve çıktı üretme görevlerini üstlenmesi test etmeyi ve değiştirmeyi zorlaştırır.

Örneğin:

def calculate_discount(price, rate):
    if price < 0:
        raise ValueError("Fiyat negatif olamaz")
    return price * (1 - rate)

Bu fonksiyon yalnızca hesaplama ve temel kuralı üstlenir. Kullanıcıdan veri alma ayrı yerde tutulabilir.

Modüller: kodu dosyalara ayırmak

Bir Python dosyası modül olarak kullanılabilir. Örneğin:

# pricing.py
def final_price(price, tax_rate):
    return price * (1 + tax_rate)

Başka dosyada:

from pricing import final_price

print(final_price(100, 0.20))

Web sürümündeki tek hücrelerde ayrı dosya oluşturmak her zaman uygun değildir; bu nedenle modül örneklerini bu hafta çoğunlukla statik olarak okuyacağız. Yerel Python ortamınızda iki dosyayla deneyin.

__name__ == "__main__" ne işe yarar?

Bir dosya doğrudan çalıştırıldığında __name__ değeri "__main__" olur. Modül olarak import edildiğinde modül adını alır.

def main():
    print("Program başladı")

if __name__ == "__main__":
    main()

Bu desen, tanımlar ile doğrudan çalıştırılan örnek/ana akışı ayırmaya yardımcı olur. Bu dönem ayrıntılı paketleme mekanizmasına girmiyoruz; 13. haftada çok dosyalı OOP sisteminde yeniden kullanacağız.

Alıştırma — return ekle

Beklenen çıktı:

0
8
def clamp_zero(value):
    if value < 0:
        return 0
    return value

Alıştırma — Yan etkiyi tahmin et

  1. Çıktıyı tahmin edin.
  2. Fonksiyonun çağıranın listesini değiştirmesini istemeseydik hangi yaklaşımı kullanabilirdik?
  3. Bu davranışın fonksiyon adından anlaşılması yeterli mi, yoksa dokümantasyon/sözleşme de gerekli mi?

Alıştırma — Büyük fonksiyonu ayır

Aşağıdaki görevi en az üç fonksiyon sorumluluğuna bölün; tam kod yazmanız gerekmiyor:

Bir CSV dosyasından ürünleri oku, fiyatı 1000 TL üzerindeki ürünleri bul, yüzde 10 indirim uygula ve sonucu ekrana yaz.

Örneğin read_products, discount_price, select_expensive gibi adlar düşünebilirsiniz. Ama her fonksiyonun tek ve açık bir işi olmasına dikkat edin.

Küçük üretim görevi — Not yardımcı modülü

Yerel ortamınızda iki dosya oluşturun:

grades.py içinde:

  • average(scores)
  • is_successful(score, limit=50)

fonksiyonlarını tanımlayın.

app.py içinde bu fonksiyonları import edip birkaç örnek veriyle kullanın. Ardından grades.py dosyasını doğrudan çalıştırdığınızda küçük bir demo gösteren main() fonksiyonu ekleyin.

Kendinizi kontrol edin

  1. print() ile return arasındaki fark nedir?
  2. Yerel değişken neden fonksiyon dışından erişilemez?
  3. Liste parametresi üzerinde append() yapmak çağıranı neden etkileyebilir?
  4. “Parametre adı aynı nesneye bağlanır” modeli neyi açıklamaya yardımcı olur?
  5. Modül ne sağlar?
  6. if __name__ == "__main__": kalıbının temel amacı nedir?

Bu haftadan akılda kalması gerekenler

  • Fonksiyon, yalnızca kod tekrarını azaltan değil, sorumluluğu adlandıran bir yapıdır.
  • return sonucu çağırana verir; print yalnızca çıktı üretir.
  • Fonksiyon çağrısında parametre adları verilen nesnelere bağlanır; mutable nesnelerde yerinde değişiklik ortak nesneyi etkileyebilir.
  • Değiştirilebilir parametrelerde yan etkiyi bilinçli yönetin.
  • Modüller programı fiziksel dosyalara ayırırken sorumluluk sınırlarını da görünür hâle getirir.
  • Bu fikirler sınıf metotları ve çok dosyalı nesne modelleri için temel olacaktır.
Back to top