Python da Fonksiyonların Kapsamı ve global Deyimi

Elimizde şöyle bir kod olduğunu düşünelim:

x = 0

def fonk():
    x = 1
    return x

Bu kodlarda, fonksiyonun dışında x adlı bir değişken var. Fonksiyonun içinde de yine x adını taşıyan başka bir değişken var. Fonksiyonumuzun görevi bu x değişkenini döndürmek.

Bu noktada size şöyle bir soru sormama izin verin: Acaba fonksiyon içinde tanımladığımız x değişkeni, fonksiyon dışındaki x değişkeninin değerini değiştiriyor mu? Bu sorunun cevabını şu kodlarla verelim:

x = 0

def fonk():
    x = 1
    return x

print('fonksiyon içindeki x: ', fonk())
print('fonksiyon dışındaki x: ', x)

Bu kodları çalıştırdığımızda şu çıktıyı alacağız:

fonksiyon içindeki x:  1
fonksiyon dışındaki x:  0

Gördüğünüz gibi fonksiyon içindeki ve fonksiyon dışındaki aynı adlı değişkenler birbirine karışmıyor. Bunun sebebi, Python’daki ‘isim alanı’ (namespace) adlı bir kavramdır.

Peki isim alanı ne demek?

Python’da değişkenlerin, fonksiyonların ve daha sonra göreceğiniz gibi sınıfların bir kapsamı vardır. Bu kapsama Python’da ‘isim alanı’ adı verilir. Dolayısıyla Python’da her nesnenin, geçerli ve etkin olduğu bir isim alanı bulunur. Örneğin yukarıdaki kodlarda fonksiyon dışındaki x değişkeni ana isim alanında yer alan ‘global’ bir değişkendir. Fonksiyon içindeki x değişkeni ise fonk() değişkeninin isim alanı içinde yer alan ‘lokal’ bir değişkendir. Bu iki değişken, adları aynı da olsa, birbirlerinden farklı iki nesnedir.

Bir de şu örneklere bakalım:

x = []
print('x'in ilk hali:', x)

def değiştir():
    print('x'i değiştiriyoruz...')
    x.append(1)
    return x

değiştir()
print('x'in son hali: ', x)

Burada ise daha farklı bir durum söz konusu. Fonksiyon içinde append() metodunu kullanarak yaptığımız ekleme işlemi fonksiyon dışındaki listeyi de etkiledi. Peki ama bu nasıl oluyor?

Python herhangi bir nesneye göndermede bulunduğumuzda, yani o nesnenin değerini talep ettiğimizde aradığımız nesneyi ilk önce mevcut isim alanı içinde arar. Eğer aranan nesneyi mevcut isim alanı içinde bulamazsa yukarıya doğru bütün isim alanlarını tek tek kontrol eder.

Birkaç örnek verelim:

def fonk():
    print(x)

fonk()

Tahmin edebileceğiniz gibi, bu kodlar şu hatayı verecektir:

Traceback (most recent call last):
  File "deneme.py", line 4, in <module>
    fonk()
  File "deneme.py", line 2, in fonk
    print(x)
NameError: global name 'x' is not defined

Bu hatanın sebebi, x adlı bir değişkenin tanımlanmamış olmasıdır. Bu hatayı gidermek için şöyle bir kod yazabiliriz:

x = 0

def fonk():
    print(x)

fonk()

Bu kod global alandaki x değişkeninin değerini verecektir.

Yukarıdaki örnekte, biz print() ile x‘in değerini sorguladığımızda Python öncelikle fonk() adlı fonksiyonun isim alanına baktı. Orada x‘i bulamayınca bu kez global alana yönelip, orada bulduğu x‘in değerini yazdırdı.

Bu durumu daha net anlayabilmek için şu kodları inceleyelim:

x = 0

def fonk():
    x = 10
    print(x)

fonk()
print(x)

Bu kodları çalıştırdığımızda 10 çıktısını alırız. Çünkü Python, dediğimiz gibi, öncelikle mevcut isim alanını kontrol ediyor. x değişkenini mevcut isim alanında bulduğu için de global alana bakmasına gerek kalmıyor.

Yalnız burada dikkat etmemiz gereken bazı şeyler var.

Dediğimiz gibi, global isim alanındaki nesnelerin değerini lokal isim alanlarından sorgulayabiliyoruz. Ancak istediğimiz şey global isim alanındaki nesnelerin değerini değiştirmekse bazı kavramlar arasındaki farkları iyi anlamamız gerekiyor.

Python’da bir nesnenin değerini değiştirmekle, o nesneyi yeniden tanımlamak farklı kavramlardır.

Eğer bir nesne değiştirilebilir bir nesne ise, o nesnenin değerini, lokal isim alanlarından değiştirebilirsiniz:

x = set()

def fonk():
    x.add(10)
    return x

print(fonk())

Ama eğer bir nesne değiştirilemez bir nesne ise, o nesnenin değerini zaten normalde de değiştiremezsiniz. Değiştirmiş gibi yapmak için ise o nesneyi yeniden tanımlamanız gerektiğini biliyorsunuz:

>>> isim = 'Emre'
>>> isim += ' Aydın'
>>> print(isim)

Emre AYDIN

Burada yaptığımız şey, karakter dizisinin değerini değiştirmekten ziyade bu karakter dizisini yeniden tanımlamaktır. Çünkü bildiğiniz gibi karakter dizileri değiştirilemeyen veri tipleridir.

İşte karakter dizileri gibi değiştirilemeyen nesneleri, lokal isim alanlarında değiştiremeyeceğiniz gibi, yeniden tanımlayamazsınız da…

isim = 'Emre'

def fonk():
    isim += ' Aydın'
    return isim

print(fonk())

Bu kodları çalıştırdığınızda Python size bir hata mesajı gösterecektir.

Aynı durum değiştirilebilir nesneler için de geçerlidir:

isim_listesi = []

def fonk():
    isim_listesi += ['Emre AYDIN', 'Oğuzhan Ak']
    return isim_listesi

print(fonk())

Değiştirilebilen bir veri tipi olan listeleri, fonksiyon içinde yeniden tanımlayamazsınız. Ancak tabii isterseniz listeleri değişikliğe uğratabilirsiniz:

isim_listesi = []

def fonk():
    isim_listesi.extend(['Emre AYDIN', 'Oğuzhan Ak']) 
return isim_listesi print(fonk())

Bu kodlar düzgün bir şekilde çalışıp, fonksiyon dışındaki isim_listesi adlı listeyi değişikliğe uğratacaktır. Ancak şu kodlar hata verecektir:

isim_listesi = []

def fonk():
    isim_listesi += ['Emre AYDIN', 'Oğuzhan Ak']
return isim_listesi print(fonk())

İşte Python programlama dili bu tür durumlar için çözüm olacak bir araç sunar bize. Bu aracın adı global.

Gelin isterseniz bu global adlı deyimin nasıl kullanılacağına bakalım önce…

Şu kodların hata vereceğini biliyorsunuz:

isim = 'Emre'

def fonk():
    isim += ' Aydın'
    return isim

print(fonk())

Ama bu kodlara şöyle bir ekleme yaparsanız işler değişir:

isim = 'Emre'

def fonk():
    global isim
    isim += ' Aydın'
    return isim

print(fonk())

Burada fonk() adlı fonksiyonun ilk satırında şöyle bir kod görüyoruz:

global isim

İşte bu satır, isim adlı değişkenin global alana taşınmasını sağlıyor. Böylece global alanda bulunan isim adlı değişkeni değişikliğe uğratabiliyoruz.

global deyimi her ne kadar ilk bakışta çok faydalı bir araçmış gibi görünse de aslında programlarımızda genellikle bu deyimi kullanmaktan kaçınmamız iyi bir fikir olacaktır. Çünkü bu deyim aslında global alanı kirletmemize neden oluyor. Global değişkenlerin lokal isim alanlarında değişikliğe uğratılması, eğer dikkatsiz davranırsanız programlarınızın hatalı çalışmasına yol açabilir.

Alıntıdır.