Fonksiyonlar yazımızda bir fonksiyonun içinde tanımladığınız bir değişkenin, fonksiyon dışından “görünmediğini” fark etmiş olabilirsiniz. Bu, tesadüf değil — Python’ın, bir değişkenin nerede “yaşayabileceğini” ve nerede “görünmez” olacağını belirleyen temel bir kuralı: kapsam (scope).
Kapsam Neden Var?
Bir değişkenin programın her yerinden erişilebilir olması, ilk bakışta kullanışlı görünebilir — ama büyük bir programda ciddi bir soruna dönüşüyor. Yüzlerce satırlık bir kodda, her değişkenin her yerden değiştirilebilir olması, hangi fonksiyonun hangi değeri ne zaman değiştirdiğini takip etmeyi neredeyse imkânsız hâle getiriyor. Kapsam kuralları, bu kaosu önlemek için bir değişkenin “yaşam alanını” sınırlıyor: bir değişken, sadece tanımlandığı bloğun (fonksiyonun) içinde bilinir; o blok bittiğinde, değişken de bellekten siliniyor.
Yerel (Local) Kapsam
Bir fonksiyonun içinde tanımlanan değişkenler, o fonksiyona özgü — yalnızca o fonksiyonun içinden erişilebiliyor:
python
def selamla():
mesaj = "Merhaba!"
print(mesaj)
selamla() # Merhaba!
print(mesaj) # HATA: mesaj tanımlı değilmesaj değişkeni, selamla() fonksiyonu çalıştığı sürece “var.” Fonksiyon işini bitirip döndüğünde, o değişken bellekten siliniyor — bu yüzden fonksiyon dışında mesaj‘a erişmeye çalıştığınızda Python bir hata veriyor. Bu davranış aslında bir avantaj: gereksiz yere bellekte tutulan değişken sayısını azaltıyor ve farklı fonksiyonların aynı isimli değişkenleri kullanmasına (birbirine karışmadan) izin veriyor.
Küresel (Global) Kapsam
Bir fonksiyonun dışında, dosyanın en üst seviyesinde tanımlanan değişkenler ise “global” kapsama sahip — programın her yerinden okunabiliyor:
python
sehir = "İstanbul"
def bilgi_yazdir():
print(f"Şehir: {sehir}")
bilgi_yazdir() # Şehir: İstanbulBurada sehir değişkeni fonksiyonun dışında tanımlı olduğu için, fonksiyonun içinden sorunsuzca okunabiliyor. Ama dikkat: okumak başka, değiştirmek başka bir şey.
Sık Yapılan Bir Hata: Global Değişkeni “Değiştiremiyorum”
Yeni başlayan pek çok Python programcısının şaşırdığı bir davranış var:
python
sayac = 0
def arttir():
sayac = sayac + 1 # HATA!
arttir()Bu kod hata veriyor, çünkü Python, fonksiyon içinde sayac‘a bir değer atadığınızı gördüğü anda, onu otomatik olarak yerel bir değişken olarak ele alıyor — ve bu yerel sayac, henüz tanımlanmadan okunmaya çalışılıyor. Python’ın burada “önce oku, sonra ata” gibi bir esneklik göstermemesinin nedeni, kodun her zaman öngörülebilir davranmasını garanti etmek.
Bu sorunu çözmek için Python’a, bu değişkenin aslında global kapsamdan geldiğini açıkça belirtmeniz gerekiyor:
python
sayac = 0
def arttir():
global sayac
sayac = sayac + 1
arttir()
print(sayac) # 1global anahtar kelimesi, “bu isim, yerel değil, global kapsamda yaşıyor ve orada değiştirilmeli” demenin resmi yolu.
İç İçe Fonksiyonlar ve nonlocal
İşler bir de fonksiyonların içinde fonksiyon tanımladığınızda ilginçleşiyor. Bu durumda ortaya üçüncü bir kapsam türü çıkıyor: “çevreleyen kapsam” (enclosing scope). İç fonksiyonun, kendisini saran (ama global olmayan) fonksiyondaki bir değişkeni değiştirmesi gerekiyorsa, nonlocal anahtar kelimesi devreye giriyor:
python
def disari():
sayi = 10
def icerde():
nonlocal sayi
sayi += 5
print(f"İçerde: {sayi}")
icerde()
print(f"Dışarıda: {sayi}")
disari()
# İçerde: 15
# Dışarıda: 15nonlocal, global‘e benzer ama bir fark var: global doğrudan en üst (modül) seviyesindeki değişkene işaret ederken, nonlocal bir ara seviyedeki, “çevreleyen” fonksiyonun değişkenine işaret ediyor.
Neden global Kullanmaktan Kaçınmalısınız?
Teknik olarak mümkün olsa da, deneyimli Python programcıları global değişkenleri sık kullanmaktan genellikle kaçınıyor. Nedeni basit: bir global değişkeni pek çok farklı fonksiyon değiştirebiliyorsa, programda bir hata bulmak, “hangi fonksiyon bu değeri ne zaman değiştirdi” sorusuna cevap bulmayı gerektiriyor — bu da kod büyüdükçe giderek zorlaşan bir hata ayıklama süreci. Bunun yerine, verileri fonksiyonlara parametre olarak geçirip, sonucu return ile geri almak, çok daha öngörülebilir ve test edilebilir bir yaklaşım. Nesne Yönelimli Programlama (OOP) yazımızda anlattığımız sınıf yapısı da, aslında büyük ölçüde bu sorunu çözmek için var: veriyi global kapsamda dağıtmak yerine, ilgili verileri ve o verilerle çalışan fonksiyonları bir sınıf içinde bir arada tutuyor.
Kapsam Zinciri: Python Bir İsmi Nasıl Arıyor?
Python, bir değişken adıyla karşılaştığında, onu belirli bir sırayla arıyor — önce en yerel (local) kapsamda, sonra varsa çevreleyen (enclosing) kapsamda, sonra global kapsamda, en son da yerleşik (built-in) fonksiyonlar arasında. Bu arama sırası, İngilizce’deki baş harflerinden “LEGB kuralı” olarak da anılıyor. Bir isim bu dört kapsamın hiçbirinde bulunamazsa, Python size “tanımlı değil” hatası veriyor.
Sonuç
Kapsam kavramı, ilk bakışta soyut bir kural gibi görünse de, aslında kodunuzun düzenli, öngörülebilir ve hatasız çalışmasının temel garantilerinden biri. Bir değişkenin “nerede yaşadığını” bilmek, özellikle programınız büyüdükçe, hangi fonksiyonun hangi veriye erişebildiğini kontrol altında tutmanın en temel yolu.