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:
- Yerel kapsam — bu fonksiyonun gövdesi.
- Çevreleyen kapsam — iç içe tanımlarda dıştaki fonksiyonun gövdesi.
- Küresel kapsam — modülün en dış düzeyi.
- 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.