İçeriğe geç
academia.sh

Ders 11 / 18

Değer ve Referans ile Aktarım

Argüman aktarım modelleri, fonksiyon içindeki değişikliğin dışarıdan görünürlüğü ve yan etki disiplini.

İçindekiler

Bir fonksiyona liste verildiğinde, fonksiyon içinde o listeye eklenen eleman çağıran tarafta görünür mü? Sorunun yanıtı “duruma göre” değildir; kesin kurallara bağlıdır ve bu kurallar, değişkenler dersinde tanıtılan iki modelin doğrudan uzantısıdır.

Bu ders o kuralları tanımlar ve karışıklığın kaynağı olan üç terimi ayırır.

Üç Model

Değerle aktarım (pass by value). Argümanın bir kopyası parametreye bağlanır. Fonksiyon içinde yapılan hiçbir değişiklik dışarıyı etkilemez. Büyük veriler için kopya maliyeti vardır.

Referansla aktarım (pass by reference). Parametre, çağıranın değişkeninin kendisine bağlanır. Fonksiyon parametreye yeni bir değer atarsa, çağıranın değişkeni de değişir.

Nesne referansının değerle aktarımı. Parametreye, nesnenin adresi kopyalanarak bağlanır. Fonksiyon ile çağıran aynı nesneyi paylaşır; ancak parametreye yeni bir nesne atamak, yalnızca yerel bağı değiştirir.

Üçüncü model en yaygın olanıdır ve en çok karıştırılanıdır. İki gözlenebilir sonucu vardır ve bunlar birbirine karşıt görünür:

def yerinde_ekle(liste: list[int]) -> None:
    """Verilen listeye eleman ekler: değişiklik dışarıdan görülür."""
    liste.append(99)

def yeniden_bagla(liste: list[int]) -> None:
    """Parametreye yeni bir liste atar: dışarısı etkilenmez."""
    liste = [0, 0, 0]

olcumler = [12, 18]

yerinde_ekle(olcumler)
print(olcumler)          # [12, 18, 99]  — nesne değişti

yeniden_bagla(olcumler)
print(olcumler)          # [12, 18, 99]  — bağ değişti, nesne değil

Fark tek cümlede özetlenir: nesneyi değiştirmek dışarıdan görülür, adı yeniden bağlamak görülmez. Fonksiyon, çağıranın değişkenine değil, o değişkenin gösterdiği nesneye erişir.

Değiştirilemez Tiplerde Fark Yok

Tipler dersinde kurulan ayrım burada işe yarar. Değiştirilemez bir değerin içeriği zaten değiştirilemez; dolayısıyla ilk davranış olanaksızdır.

def artir(sayi: int) -> int:
    sayi += 1            # yalnızca yerel bağ değişti
    return sayi

deger = 10
print(artir(deger), deger)      # 11 10

Sayılar, dizgiler ve benzeri değiştirilemez tipler bu nedenle her modelde aynı davranır. Karışıklık yalnızca değiştirilebilir tiplerde ortaya çıkar.

Yan Etki

Bir fonksiyonun, dönüş değeri dışında dış dünyada bıraktığı ize yan etki denir. Argümanı değiştirmek, küresel bir değişkeni güncellemek, ekrana yazmak, dosyaya yazmak — hepsi yan etkidir.

Yan etki kendiliğinden kötü değildir; program sonunda bir iş yapmak zorundadır. Sorun, yan etkinin beklenmemesidir. ortalama adlı bir fonksiyonun listeyi sıralaması, çağıranın hesaba katmadığı bir değişiklik üretir.

Aynı işi iki farklı sözleşmeyle yazmak mümkündür:

def sirali_kopya(olcumler: list[int]) -> list[int]:
    """Sıralanmış YENİ bir liste döndürür; girdi değişmez."""
    return sorted(olcumler)

def yerinde_sirala(olcumler: list[int]) -> None:
    """Verilen listeyi YERİNDE sıralar; değer döndürmez."""
    olcumler.sort()

a = [12, 18, 7]
b = sirali_kopya(a)
print(a, b)              # [12, 18, 7] [7, 12, 18]

yerinde_sirala(a)
print(a)                 # [7, 12, 18]

İki yazım da meşrudur. Ayırt edici olan, sözleşmenin adda ve belgede görünür olmasıdır. Yerinde değiştiren fonksiyonlar genellikle değer döndürmez; bu, bir önceki dersteki komut–sorgu ayrımının uygulanmış hâlidir. Değer döndüren bir çağrının aynı zamanda girdiyi değiştirmesi, iki sözleşmeyi birbirine karıştırır.

Değişken Sayıda Argüman

Bazı fonksiyonların kaç argüman alacağı önceden belli değildir. Diller bunun için, kalan argümanları tek bir koleksiyonda toplayan bir yazım sunar:

def en_buyugu(*degerler: int) -> int:
    """Verilen değerlerin en büyüğünü döndürür. En az bir değer gerekir."""
    if not degerler:
        raise ValueError("en az bir değer gerekir")
    en_buyuk = degerler[0]
    for deger in degerler:
        if deger > en_buyuk:
            en_buyuk = deger
    return en_buyuk

print(en_buyugu(12, 18, 7))          # 18
print(en_buyugu(*[12, 18, 7, 25]))   # 25  — liste tek tek argümana açıldı

İkinci çağrıdaki yıldız, bir koleksiyonu ayrı argümanlara açar. Aynı simge, tanımda argümanları toplar, çağrıda ise dağıtır; iki kullanım birbirinin tersidir.

Bu esnekliğin bedeli, sözleşmenin belirsizleşmesidir: argüman sayısı imzadan okunamaz. Argümanların anlamı farklıysa — biri sınır, diğeri ölçüm listesi gibi — ayrı parametreler her zaman daha açıktır. Değişken sayıda argüman, yalnızca aynı türden ve eşdeğer rolde değerler için uygundur.

Savunma Kopyası

Bir fonksiyon, aldığı veriyi saklayacaksa ve çağıranın onu sonradan değiştirmesi sorun yaratacaksa, kopyasını almalıdır. Bu, savunma kopyası olarak adlandırılır.

class OlcumKaydi:
    def __init__(self, olcumler: list[int]) -> None:
        self.olcumler = list(olcumler)     # savunma kopyası

disaridaki = [12, 18]
kayit = OlcumKaydi(disaridaki)
disaridaki.append(99)                      # dışarıdaki liste değişti

print(kayit.olcumler)                      # [12, 18]  — kayıt etkilenmedi

Kopya alınmasaydı, kaydın içeriği dışarıdan sessizce değiştirilebilirdi. Buradaki class yapısı bu kursta yalnızca örnek olarak kullanılmıştır; paradigmalar konusunda tanıtılacak.

Kopyanın bedeli bellek ve zamandır. Değişkenler dersinde belirtilen sığ–derin kopya ayrımı burada da geçerlidir: iç içe yapılarda yalnızca dış kabuğu kopyalamak, paylaşımı tam olarak kırmaz.

Aynı sorun ters yönde de vardır: bir fonksiyon, sakladığı koleksiyonu doğrudan döndürüyorsa iç durumunu dışarı sızdırmış olur. Çağıran, dönen koleksiyonu değiştirerek nesnenin içini kurallara uymadan güncelleyebilir. Bu, savunma kopyasının dönüş tarafındaki karşılığıdır: dışarı verilen koleksiyon ya kopyalanır ya da değiştirilemez bir görünüm olarak sunulur.

Üçüncü bir seçenek, veriyi değiştirilemez bir tiple taşımaktır. Böyle bir veri kopyalanmak zorunda değildir; paylaşımı güvenlidir. Fonksiyonel programlama yaklaşımının bu tercihi, paradigmalar konusunda ele alınacaktır.

Değiştirmeme Sözünü Tipe Yazmak

Bazı diller, bir parametrenin değiştirilmeyeceğini tip düzeyinde bildirmeye izin verir. Bildirim yapıldığında, gövde içinde o değeri değiştiren bir işlem derleme hatası üretir.

Bunun iki kazancı vardır. Sözleşme belgede değil, imzada durur; okuyanın belgeye başvurması gerekmez. Ve söz, kanıtlanmış olur — gelecekte gövdeye eklenen bir satır sözü sessizce bozamaz.

Böyle bir bildirim sunmayan dillerde aynı söz, yalnızca belge ve inceleme disipliniyle korunur. Bu durumda değiştirilemez tipleri tercih etmek, sözü uygulanabilir kılmanın en güvenilir yoludur.

Hangi Modelin Kullanıldığını Anlamak

Bir dilin hangi modeli kullandığı, iki küçük denemeyle anlaşılır:

  1. Fonksiyon içinde parametreye yeni bir değer atayın. Çağıranın değişkeni değişiyorsa referansla aktarım vardır.
  2. Fonksiyon içinde nesneyi yerinde değiştirin. Çağıran tarafta görünüyorsa nesne paylaşılıyor demektir.

İkisi de görünmüyorsa değerle aktarım; yalnızca ikincisi görünüyorsa nesne referansının değerle aktarımı söz konusudur. Bu deneme, belgeye başvurmadan modeli belirlemenin en kısa yoludur.

Özet

  • Değerle aktarımda kopya geçer, referansla aktarımda çağıranın değişkeninin kendisi geçer, nesne referansının değerle aktarımında adres kopyalanır ve nesne paylaşılır.
  • En yaygın model üçüncüsüdür: nesneyi değiştirmek dışarıdan görülür, parametreyi yeniden bağlamak görülmez.
  • Değiştirilemez tiplerde bu ayrım gözlenmez; karışıklık yalnızca değiştirilebilir tiplerde doğar.
  • Yan etki, dönüş değeri dışındaki her izdir; sorun yan etkinin varlığı değil, beklenmemesidir.
  • Yerinde değiştiren ve yeni değer üreten iki sözleşme de meşrudur; seçim adda ve belgede görünür kılınır.
  • Saklanacak veri için savunma kopyası alınır; değiştirilemez tipler bu ihtiyacı ortadan kaldırır.

Sonraki Adım

Fonksiyon gövdesinde tanımlanan adlar dışarıdan görünmez; dışarıdaki adlar ise gövde içinden okunabilir. Bu görünürlük kurallarının tam tanımı nedir ve bir ad birden çok yerde tanımlıysa hangisi kazanır? Sonraki ders kapsam ve yaşam süresi kurallarını ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat