Python bir adı ararken sırasıyla dört kapsama bakar: önce fonksiyonun kendi yerel (local) adları, sonra varsa onu içeren fonksiyonların adları (enclosing), sonra modülün global adları, en son da print, len gibi yerleşik (built-in) adlar. Bu sıra baş harflerinden ötürü kısaca LEGB diye anılır. Bir fonksiyonun içinde bir ada atama yaptığınız anda o ad, fonksiyonun tamamında yerel sayılır. Global bir değişkeni fonksiyon içinden yeniden bağlamak için global, iç içe fonksiyonlarda dıştaki fonksiyonun değişkenini değiştirmek için nonlocal kullanılır.
Bu yazıda kapsam kurallarını resmî Python belgelerindeki (Tutorial'ın sınıflar bölümü, Language Reference'ın Execution model ve Simple statements bölümleri, Programming FAQ) tanımlara dayanarak anlatıyoruz: ad alanı nedir, UnboundLocalError neden çıkar, closure nasıl çalışır, döngüde oluşturulan lambda'lar neden aynı sonucu verir ve sınıf gövdesi neden özel bir durumdur. Bütün örnekler Python 3.15.2 ile çalıştırıldı. Fonksiyon temelleri için def ile fonksiyon tanımlama yazısına bakabilirsiniz.
Ad alanı ve kapsam: iki temel kavram
Tutorial'a göre bir ad alanı (namespace), adlardan nesnelere bir eşlemedir. Yerleşik adlar kümesi, bir modülün global adları ve bir fonksiyon çağrısının yerel adları birer ad alanıdır. Farklı ad alanlarındaki adlar arasında hiçbir ilişki yoktur: İki modül aynı adlı bir fonksiyon tanımlayabilir ve karışıklık olmaz, çünkü kullanan taraf modul.fonksiyon biçiminde yazar.
Ad alanlarının ömürleri farklıdır. Yerleşik adlar yorumlayıcı açılırken oluşur ve hiç silinmez. Bir modülün global ad alanı modül okunduğunda oluşur ve genellikle program bitene kadar yaşar; doğrudan çalıştırdığınız betik __main__ adlı bir modül sayılır. Bir fonksiyonun yerel ad alanı ise fonksiyon çağrıldığında oluşur, fonksiyon döndüğünde veya içinde yakalanmayan bir istisna yükseldiğinde ortadan kalkar; özyinelemeli her çağrının kendi yerel ad alanı vardır.
Kapsam (scope) ise bir ad alanının doğrudan, yani modul. gibi bir önek olmadan erişilebildiği program metni bölgesidir. Belgelerin vurguladığı nokta, kapsamların metne bakılarak, yani statik olarak belirlenmesidir: Bir fonksiyonun global kapsamı, fonksiyon nereden çağrılırsa çağrılsın, tanımlandığı modüldür.
LEGB: Python bir adı nerede arar?

Çalışma sırasında aynı anda erişilebilen iç içe kapsamlar şunlardır ve şu sırayla aranır:
| Sıra | Kapsam | İçerdiği adlar |
|---|---|---|
| 1 | Local (yerel) | Çalışan fonksiyonun parametreleri ve içinde atanan adlar |
| 2 | Enclosing (çevreleyen) | İç içe fonksiyonlarda dıştaki fonksiyonların yerel adları, en yakından başlayarak |
| 3 | Global | Modülün üst seviyesinde tanımlanan adlar |
| 4 | Built-in (yerleşik) | print, len, list, ValueError gibi builtins modülündeki adlar |
mesaj = 'global'
def dis():
ek = 'dış fonksiyon'
def ic():
yerel = 'iç fonksiyon'
print(yerel, '|', ek, '|', mesaj, '|', len('abc'))
ic()
dis() # çıktı: iç fonksiyon | dış fonksiyon | global | 3ic() içinde dört ad dört farklı kapsamdan bulundu: yerel kendi kapsamından, ek dıştaki fonksiyondan, mesaj modülden, len ise yerleşik adlardan. Ad hiçbir kapsamda yoksa NameError yükselir. Python'da if, for veya while blokları yeni bir kapsam oluşturmaz; Execution model belgesine göre kod blokları yalnızca modül, fonksiyon gövdesi ve sınıf tanımıdır. Bir if içinde atanan değişken, blok bittikten sonra da aynı fonksiyonda kullanılabilir.
Yerleşik bir adı gölgelemek
Arama sırası yerel ve globalden yerleşiğe doğru ilerlediği için, yerleşik bir adla aynı adı taşıyan bir değişken tanımlarsanız o kapsamda yerleşik ad görünmez olur. En sık görülen örnek bir değişkene list, str veya sum adını vermektir:
list = [1, 2] # yerleşik list gölgelendi
try:
list('abc')
except TypeError as e:
print(e) # çıktı: 'list' object is not callable
del list # global adı sil, yerleşik yeniden görünür
print(list('abc')) # çıktı: ['a', 'b', 'c']Atama yapılan ad yereldir: UnboundLocalError
Kapsam kurallarının en şaşırtıcı sonucu şudur: Execution model belgesine göre bir kod bloğunun herhangi bir yerinde bir ada atama yapılıyorsa, o bloktaki bütün kullanımlar bu yerel ada atıf sayılır. Python'da değişken bildirimi olmadığı için yorumlayıcı yerel adları, bloğun tamamını tarayıp atamaları bularak belirler. Bu yüzden atamadan önce gelen bir okuma bile global değişkeni değil, henüz değeri olmayan yerel değişkeni okumaya çalışır. Programming FAQ'daki örneğin Türkçe uyarlaması:
sayac = 10
def oku():
print(sayac) # yalnızca okuma: global sayac kullanılır
oku() # çıktı: 10
def artir():
print(sayac) # sayac aşağıda atandığı için yerel sayılır
sayac += 1
artir()
# hata: UnboundLocalError: cannot access local variable 'sayac' where it is not associated with a valuesayac += 1 bir atama olduğu için derleyici sayac'ı artir()'ın yerel değişkeni olarak işaretler; önceki print(sayac) değeri olmayan bu yerel değişkene ulaşmaya çalışır ve hata alır. UnboundLocalError, NameError'ın bir alt sınıfıdır. FAQ bu tasarımın gerekçesini de açıklar: Atanan değişkenler için global zorunluluğu istenmeyen yan etkilere karşı bir engel oluşturur; her okumada global istenseydi, her yerleşik fonksiyon ve içe aktarılan modül için de bu bildirimi yazmanız gerekirdi.
global: modül değişkenini fonksiyondan değiştirmek
global ad deyimi, o fonksiyon içindeki ad kullanımlarının modülün global adına gitmesini sağlar. Ad modülde yoksa, atama yapıldığında global ad alanında yeni bir değişken oluşur. Simple statements belgesine göre global deyimi ait olduğu kapsamın tamamına uygulanır ve adın o kapsamdaki ilk kullanımından önce yazılmalıdır.
sayac = 0
def artir():
global sayac
sayac += 1
artir()
artir()
print(sayac) # çıktı: 2
def olustur():
global yeni_ad
yeni_ad = 'fonksiyon içinde oluştu'
olustur()
print(yeni_ad) # çıktı: fonksiyon içinde oluştuglobal deyimini yanlış yere koymak derleme aşamasında yakalanır. Python 3.15.2'nin verdiği mesajlar şunlardır: ad global'dan önce okunursa SyntaxError: name 'x' is used prior to global declaration, önce atanırsa SyntaxError: name 'x' is assigned to before global declaration, aynı ad hem parametre hem global yapılırsa SyntaxError: name 'a' is parameter and global. Modül seviyesinde bütün değişkenler zaten global olduğu için orada global yazmanın bir etkisi yoktur.
Değiştirmek ile yeniden bağlamak aynı şey değil
global yalnızca adı yeniden bağlamak (yeni bir nesneye atamak) için gerekir. Global bir listeye eleman eklemek veya bir sözlüğe anahtar yazmak, adı değiştirmez, aynı nesneyi yerinde değiştirir; bu yüzden global gerektirmez. Tutorial'ın ifadesiyle atamalar veriyi kopyalamaz, yalnızca adları nesnelere bağlar.
kayitlar = []
def ekle(x):
kayitlar.append(x) # nesneyi yerinde değiştirir, global gerekmez
def sifirla():
kayitlar = [] # yeni bir YEREL ad oluşturur
ekle('a')
sifirla()
print(kayitlar) # çıktı: ['a']sifirla() global listeyi boşaltmadı; yalnızca kendi içinde aynı adlı yeni bir liste oluşturdu ve fonksiyon bitince bu liste kayboldu. Global listeyi gerçekten boşaltmak için kayitlar.clear() yazabilir ya da global kayitlar bildirip yeniden atayabilirsiniz.
nonlocal: dıştaki fonksiyonun değişkenini değiştirmek

İç içe fonksiyonlarda iç fonksiyon, dıştakinin değişkenlerini okuyabilir ama atama yaptığında yine kendi yerel değişkenini oluşturur. Dıştaki fonksiyonun değişkenini yeniden bağlamak için Python 3 ile gelen (PEP 3104) nonlocal deyimi kullanılır. Simple statements belgesine göre ad, çevreleyen fonksiyon kapsamlarından birinde önceden bağlanmış olmalıdır; birden fazla kapsamda varsa en yakını kullanılır. Ad hiçbir çevreleyen fonksiyonda yoksa SyntaxError: no binding for nonlocal 'x' found, modül seviyesinde kullanılırsa SyntaxError: nonlocal declaration not allowed at module level alırsınız.
def sayac_olustur():
deger = 0
def artir():
nonlocal deger
deger += 1
return deger
return artir
s = sayac_olustur()
print(s(), s(), s()) # çıktı: 1 2 3
s2 = sayac_olustur()
print(s2()) # çıktı: 1
print(s.__code__.co_freevars) # çıktı: ('deger',)
print(s.__closure__[0].cell_contents) # çıktı: 3Her sayac_olustur() çağrısı kendi deger değişkenine sahip yeni bir artir fonksiyonu üretir; s ve s2 birbirini etkilemez. Bu yapıya closure denir: İç fonksiyon, dış fonksiyon bittikten sonra da kullandığı dış değişkenleri taşır. Data model belgesine göre bu bağlar fonksiyonun __closure__ özelliğindeki hücrelerde (cell) durur ve her hücrenin değeri cell_contents ile okunabilir. Global ve yerleşik adlar bu hücrelere dahil değildir.
Üç atama türünü yan yana görmek
Tutorial'daki scope_test örneği yerel, nonlocal ve global atamanın farkını tek seferde gösterir. Aşağıda aynı yapıyı Türkçe adlarla kurduk:
def kapsam_testi():
def yerel_ata():
deger = 'yerel'
def nonlocal_ata():
nonlocal deger
deger = 'nonlocal'
def global_ata():
global deger
deger = 'global'
deger = 'test'
yerel_ata()
print('Yerel atamadan sonra:', deger)
nonlocal_ata()
print('nonlocal atamadan sonra:', deger)
global_ata()
print('global atamadan sonra:', deger)
kapsam_testi()
print('Modül kapsamında:', deger)
# çıktı: Yerel atamadan sonra: test
# çıktı: nonlocal atamadan sonra: nonlocal
# çıktı: global atamadan sonra: nonlocal
# çıktı: Modül kapsamında: globalYerel atama dış fonksiyonun deger'ini etkilemedi. nonlocal ataması onu değiştirdi. global ataması ise fonksiyonun değişkenine dokunmadı, modül seviyesindeki deger'i oluşturdu; zaten öncesinde modülde böyle bir ad yoktu.
Serbest değişkenler çağrı anında okunur
Bir blokta kullanılıp orada tanımlanmayan değişkene serbest değişken (free variable) denir. Execution model belgesine göre serbest değişkenlerin çözümlenmesi derleme anında değil, çalışma anında yapılır. Yani fonksiyon, değişkenin tanım anındaki değerini değil, çağrıldığı andaki değerini görür:
i = 10
def f():
print(i)
i = 42
f() # çıktı: 42Döngüde oluşturulan lambda tuzağı
Bu kuralın en bilinen sonucu, Programming FAQ'da da yer alan döngü içi lambda sorunudur. Bir döngüde fonksiyonlar oluşturup döngü değişkenini kullanırsanız, fonksiyonlar çağrıldığında döngü çoktan bitmiştir ve hepsi değişkenin son değerini görür. Çözüm, değeri varsayılan parametreyle yakalamaktır; varsayılan değerler tanım anında hesaplandığı için her fonksiyon o turdaki değeri saklar. FAQ, bu davranışın lambda'ya özgü olmadığını, normal fonksiyonlar için de geçerli olduğunu belirtir.
kareler = []
for x in range(5):
kareler.append(lambda: x ** 2)
print(kareler[2](), kareler[4]()) # çıktı: 16 16
x = 8
print(kareler[2]()) # çıktı: 64
kareler = []
for x in range(5):
kareler.append(lambda n=x: n ** 2)
print(kareler[2](), kareler[4]()) # çıktı: 4 16Sınıf gövdesi özel bir kapsamdır
Execution model belgesi sınıf tanımlarını ayrıca ele alır. Sınıf gövdesinde tanımlanan adların kapsamı yalnızca sınıf bloğuyla sınırlıdır; sınıfın metotlarına uzanmaz. Bir metodun içinden sınıf değişkenine ulaşmak için self.ad veya SinifAdi.ad yazmanız gerekir. Python 3.15.2'nin hata mesajı bu durumda doğru yazımı önerir:
class Vergi:
oran = 20
def hesapla(self, tutar):
return tutar * oran / 100 # self.oran olmalı
print(Vergi().hesapla(100))
# hata: NameError: name 'oran' is not defined. Did you mean: 'self.oran'?Aynı kural sınıf gövdesindeki comprehension'lar ve generator ifadeleri için de geçerlidir, çünkü onların da kendi kapsamları vardır. Belgelerdeki örneğe göre sınıf gövdesinde a = 42 tanımlayıp hemen altında [a + i for i in range(3)] yazmak NameError: name 'a' is not defined verir. Sınıflar ve self class ve nesne yazısında ayrıntılı anlatılıyor.
Döngü değişkeni sızar, comprehension değişkeni sızmaz
Bir for döngüsünün değişkeni, döngünün bulunduğu kapsamın değişkenidir ve döngü bittikten sonra son değeriyle kalır. List comprehension içindeki değişken ise comprehension'ın kendi kapsamındadır; dışarıdan görünmez ve aynı adlı bir dış değişkeni bozmaz.
for k in range(3):
pass
print(k) # çıktı: 2
m = 'dokunulmadı'
kareler = [m * m for m in range(3)]
print(kareler, m) # çıktı: [0, 1, 4] dokunulmadı
if True:
blok_ici = 'if bloğu kapsam oluşturmaz'
print(blok_ici) # çıktı: if bloğu kapsam oluşturmazDöngülerin bu ve benzeri ayrıntıları for ve while yazısında, comprehension yazımı list comprehension yazısında var.
Modüller arasında değişken paylaşmak
global yalnızca bir modülün kendi global ad alanını ilgilendirir; başka bir modülün değişkenini değiştirmez. Programming FAQ, tek bir program içinde modüller arasında bilgi paylaşmanın alışılmış yolunun ayrı bir yapılandırma modülü (örneğin config.py) oluşturmak olduğunu söyler: Her modül import config yazar ve değeri config.x = 1 biçiminde, modül niteliği olarak okur veya değiştirir. Her modülün yalnızca bir örneği olduğu için yapılan değişiklik her yerde görünür. from config import x yazarsanız ise kendi modülünüzde x adında ayrı bir bağ oluşur ve sonraki değişiklikleri görmezsiniz. İçe aktarma kuralları modül ve paket yazısında.
global ve nonlocal karşılaştırması

| global | nonlocal | |
|---|---|---|
| Hedef kapsam | Modülün global ad alanı | En yakın çevreleyen fonksiyon |
| Ad önceden var olmalı mı? | Hayır, atama yeni global oluşturur | Evet, yoksa SyntaxError |
| Modül seviyesinde | Etkisiz ama yazılabilir | SyntaxError |
| Tipik kullanım | Modül düzeyindeki bir ayarı veya sayacı güncellemek | Closure içinde durum tutmak (sayaç, önbellek) |
| Yalnızca okumak için gerekli mi? | Hayır | Hayır |
Sık yapılan hatalar
- Global değişkeni fonksiyonda artırmak:
sayac += 1bir atamadır;UnboundLocalErroralırsınız. Yaglobalbildirin ya da değeri parametreyle alıpreturnedin. - Her şey için global kullanmak: Fonksiyonların gizli yan etkileri olur ve test etmek zorlaşır; çoğu durumda parametre ve dönüş değeri daha temizdir.
- Yerleşik adları gölgelemek:
list,str,sum,idgibi adları değişken adı olarak kullanmayın. - Döngüde lambda veya iç fonksiyon oluşturup değişkenin donacağını sanmak: Değer çağrı anında okunur; varsayılan parametreyle yakalayın.
- Metot içinde sınıf değişkenine çıplak adla ulaşmak:
self.adveyaSinif.adyazın. - Global listeyi yeniden atayarak boşaltmaya çalışmak:
liste = []yerel ad oluşturur;liste.clear()kullanın.
Sık Sorulan Sorular
LEGB kuralı nedir?
Python'un bir adı arama sırasıdır: Local, Enclosing, Global, Built-in. Ad ilk bulunduğu kapsamdan alınır; hiçbirinde yoksa NameError yükselir.
UnboundLocalError neden çıkar?
Fonksiyonun herhangi bir yerinde bir ada atama yapıldığı için o ad yerel sayılır, ama siz onu atamadan önce okumaya çalışırsınız. Global değişkeni kastediyorsanız global bildirin; değilse değişkeni okumadan önce yerel olarak tanımlayın.
global ile nonlocal arasındaki fark nedir?
global modül seviyesindeki adı, nonlocal ise iç içe fonksiyonlarda en yakın dış fonksiyonun adını yeniden bağlar. nonlocal için adın dış fonksiyonda önceden tanımlı olması gerekir.
Python'da blok kapsamı var mı?
Hayır. if, for, while ve with blokları yeni bir kapsam oluşturmaz; kapsam oluşturan bloklar modül, fonksiyon gövdesi ve sınıf tanımıdır. Comprehension'ların ise kendi kapsamı vardır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Python öğrenme rehberi · def ile fonksiyon tanımlama · lambda, map ve sorted · Decorator yazma · class ve nesne · Modül ve paket import · Sık hata mesajları