İçeriğe geç
academia.sh

Ders 12 / 18

Kapsam ve Yaşam Süresi

Ad arama sırası, gölgeleme, küresel duruma yazma, yerel değişkenlerin ömrü ve kapanışlar.

İçindekiler

Önceki ders, fonksiyonun dışarıdaki nesneleri değiştirebildiğini gösterdi. Bu, doğal bir soruyu doğurur: fonksiyon gövdesinde yazılan bir ad, hangi değişkeni gösterir? Gövdede tanımlanan adlar dışarıdan görünür mü?

Bu dersin konusu kapsam — bir adın geçerli olduğu bölge — ve yaşam süresi — bir değerin var olduğu zaman aralığıdır. İki kavram sık karıştırılır; biri görünürlükle, diğeri ömürle ilgilidir.

Yerel Kapsam

Fonksiyon gövdesinde bağlanan adlar yereldir: yalnızca o gövde içinde görünür ve fonksiyon döndüğünde ortadan kalkar.

def ozetle(olcumler: list[int]) -> float:
    toplam = 0                     # yerel ad
    for olcum in olcumler:         # döngü değişkeni de yerel
        toplam += olcum
    return toplam / len(olcumler)

print(ozetle([12, 18]))            # 15.0
# print(toplam)                    -> hata: 'toplam' burada tanımlı değil

Bu, önceki kursun bellek düzeni dersinin doğrudan sonucudur: yerel değişkenler çağrı çerçevesinde durur ve çerçeve, dönüşte bırakılır. Yerelliğin yararı yalıtımdır — iki ayrı fonksiyon aynı adı, birbirini etkilemeden kullanabilir.

Ad Arama Sırası

Bir ad gövdede kullanıldığında, dil onu belirli bir sırayla arar. Yaygın sıra şudur:

  1. Yerel kapsam — bu fonksiyonun gövdesi.
  2. Çevreleyen kapsam — iç içe tanımlarda dıştaki fonksiyonun gövdesi.
  3. Küresel kapsam — modülün en dış düzeyi.
  4. Yerleşik adlar — dilin kendi sağladığı adlar.

Arama, ilk bulunan yerde durur. Bu nedenle bir ad hem yerelde hem küresel düzeyde tanımlıysa, gövde içinde yerel olan kazanır.

SINIR = 20                          # küresel

def asanlari_say(olcumler: list[int]) -> int:
    sayac = 0                       # yerel
    for olcum in olcumler:
        if olcum > SINIR:           # küresel addan okunur
            sayac += 1
    return sayac

print(asanlari_say([12, 25, 30]))   # 2

Küresel bir addan okumak serbesttir. Yazmak ise farklıdır ve bir sonraki bölümün konusudur.

Gölgeleme

Dıştaki bir adla aynı adı yerelde kullanmaya gölgeleme denir. Dıştaki ad erişilemez hâle gelir; yok olmaz, yalnızca o bölgede görünmez.

SINIR = 20

def yanlis_kullanim(olcumler: list[int]) -> int:
    SINIR = 5                       # dıştaki adı gölgeler
    return len([o for o in olcumler if o > SINIR])

print(yanlis_kullanim([12, 25, 30]))   # 3   — beklenen 2 değil
print(SINIR)                            # 20  — küresel değer değişmedi

Gölgeleme her zaman hata değildir; kısa gövdelerde bilinçli olarak kullanılabilir. Hata kaynağı olduğu durum, gölgelemenin farkında olunmamasıdır: okuyucu dıştaki değeri sanırken kod yereli kullanır. Adlandırma dersindeki ölçüt burada da geçerlidir — ayrı kavramlar ayrı adlarla anılır.

Küresel Duruma Yazmak

Bir fonksiyonun küresel değişkeni değiştirmesi, önceki dersteki yan etki sınıfına girer ve genellikle kaçınılır. Çoğu dil bunu zorlaştırır: küresel bir ada yazmak için açık bir bildirim gerekir; aksi hâlde atama yeni bir yerel ad oluşturur.

Kaçınmanın gerekçesi üç maddedir. Fonksiyonun davranışı yalnızca argümanlarına bağlı olmaktan çıkar; aynı çağrı, küresel durumun geçmişine göre farklı sonuç verebilir. Sınama zorlaşır, çünkü test her çalıştırmada durumu sıfırlamak zorundadır. Eşzamanlı çalışan iki iş parçacığı aynı küresel değeri değiştirdiğinde, sonuç sıraya bağlı hâle gelir.

Alternatif, gereken değeri argüman olarak almak ve sonucu döndürmektir. Bu, fonksiyonu bağımsız ve sınanabilir kılar.

Sabitler bu kuralın dışındadır: değişmeyen küresel değerler paylaşılabilir ve okunabilirliği artırır.

Yaşam Süresi ve Kapanışlar

Kapsam görünürlüğü, yaşam süresi ise ömrü belirtir. İkisi çoğu zaman örtüşür: yerel değişken çerçeveyle birlikte gelir, çerçeveyle birlikte gider.

Örtüşmenin bozulduğu bir durum vardır. İç içe tanımlanan bir fonksiyon, dıştaki fonksiyonun yerel değişkenine erişiyorsa, o değişken dıştaki fonksiyon döndükten sonra da yaşamayı sürdürmek zorundadır. Bu yapıya kapanış (closure) denir.

def sinir_denetleyicisi(sinir: int):
    """Verilen sınıra göre karar veren bir fonksiyon üretir."""
    def asiyor_mu(olcum: int) -> bool:
        return olcum > sinir        # dıştaki 'sinir' yakalandı
    return asiyor_mu

yirmi_asan = sinir_denetleyicisi(20)
on_asan = sinir_denetleyicisi(10)

print(yirmi_asan(25), yirmi_asan(15))   # True False
print(on_asan(15))                      # True

sinir_denetleyicisi çağrısı bittiğinde çerçevesi bırakılır, ancak sinir değeri yaşamayı sürdürür: üretilen fonksiyon ona erişmektedir. Önceki kursun terimleriyle, yakalanan değer yığıtta değil öbekte yaşar ve ona erişen fonksiyon var oldukça toplanmaz.

Yakalanan değeri iç fonksiyondan değiştirmek ayrı bir bildirim gerektirir. Kural, küresel adlarda olduğu gibidir: bildirim olmadan yapılan atama, dıştaki adı güncellemek yerine yeni bir yerel ad oluşturur. Bu ayrım, sayaç tutan kapanışlarda sık karşılaşılan bir hata kaynağıdır — sayaç artıyor görünür, ama her çağrıda sıfırdan başlar.

Kapanışlar, davranışı parametreyle üretmenin sade bir yoludur; aynı fikir, nesneye dayalı yaklaşımda durumu bir nesnede saklamakla karşılanır. İki yaklaşımın karşılaştırması paradigmalar konusunda yapılacaktır.

Modüller ve Ad Alanları

Küresel kapsam sanıldığı kadar geniş değildir: her kaynak dosya kendi ad alanına sahiptir. Bir dosyada tanımlanan ad, başka bir dosyada kendiliğinden görünmez; içe aktarılması gerekir.

İçe aktarma, ada yeni bir bağ kurar. İki biçim vardır ve ikisinin okunabilirlik etkisi farklıdır:

import math                      # modül adı üzerinden erişim
print(math.sqrt(16))             # 4.0

from math import sqrt            # adı doğrudan kendi ad alanımıza al
print(sqrt(16))                  # 4.0

İlk biçimde sqrt adının nereden geldiği çağrıda görünür. İkinci biçimde çağrı kısalır, ancak adın kaynağı görünmez olur ve aynı adı taşıyan iki kaynak birbirini gölgeleyebilir.

Ad alanları, çakışmaları önlemenin yapısal yoludur: iki farklı modül aynı adı, farklı anlamlarla kullanabilir. Aynı fikrin daha büyük ölçeği paket ve ad alanı hiyerarşileridir ve dil müfredatlarında ayrıntılandırılır.

Küresel kapsamla ilgili kural burada da geçerlidir: modül düzeyinde tanımlanan değiştirilebilir durum, dosyanın her yerinden erişilebilir olduğu için aynı sınama ve eşzamanlılık sorunlarını üretir.

Değişkenin Ömrünü Kısa Tutmak

Pratik bir kural, bu dersin tamamını özetler: bir ad, gerektiği en dar bölgede tanımlanır. Döngü içinde kullanılan bir değişken döngü içinde, fonksiyona ait bir değişken fonksiyon içinde tanımlanır.

Bu kural, kapsamı bir tasarım aracı hâline getirir. Dar kapsamın üç yararı vardır. Okuyucunun akılda tutması gereken ad sayısı azalır. Adın nerede değiştirilebileceği sınırlanır, bu da hata aramayı kolaylaştırır. Ve gölgeleme ile ad çakışması olasılığı düşer.

Özet

  • Fonksiyon gövdesinde bağlanan adlar yereldir ve çağrı çerçevesiyle birlikte ortadan kalkar.
  • Ad araması yerelden başlar, çevreleyen ve küresel kapsamlarla sürer, yerleşik adlarla biter; ilk bulunan kazanır.
  • Gölgeleme, dıştaki adı erişilemez kılar; tehlikesi fark edilmemesidir.
  • Küresel duruma yazmak fonksiyonu argümanlarından bağımsız hâle getirir ve sınamayı zorlaştırır; değer argümanla alınır, sonuç döndürülür.
  • Kapanış, dıştaki yerel değişkeni yakalayan iç fonksiyondur; yakalanan değer, dış çağrı bittikten sonra da yaşar.
  • Adlar gerektiği en dar bölgede tanımlanır.

Sonraki Adım

Bir fonksiyonun kendi kendini çağırması, çağrı yığıtında ilginç bir durum yaratır: aynı fonksiyonun birden çok çerçevesi aynı anda var olur. Sonraki ders özyinelemeyi — taban durumu, indirgeme adımı ve yığıt derinliği — 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