10.5 ˙Isimli Argümanlar
10.9 Fonksiyonların Kapsamı ve global Deyimi
Bu bölümde Python’daki “isim alanı” (namespace) ve kapsam (scope) kavramından söz ede-ce˘giz biraz... Kabaca ifade etmek gerekirse “isim alanı” denen ¸sey, Python’daki herhangi bir nesnenin etki alanıdır. Python’daki nesnelerin birer kapsama alanı vardır. Örne˘gin Python’daki fonksiyonlar birer nesnedir. Esasında Python’daki her ¸sey bir nesnedir. ¸Simdilik bu “nesne”
meselesini fazla kafanıza takmayın. Bu konuyu yeri geldi˘ginde esaslı bir ¸sekilde inceleye-ce˘giz... Ne diyorduk? Evet, Python’daki fonksiyonlar birer nesnedir ve bu nesnelerin de bir kapsama alanı vardır. Ya da ba¸ska bir deyi¸sle fonksiyonlar belirli bir isim alanı içinde yer alır.
Hemen ufak bir örnek verelim:
def deneme():
sayı = 5 print(sayı)
Burada “sayı” adlı de˘gi¸sken, deneme() adlı fonksiyonun isim alanı içinde yer alır. Yani bu de˘gi¸skenin kapsamı deneme() adlı fonksiyonla sınırlıdır. “sayı” adlı de˘gi¸sken bu fonksiyonun dı¸sında var olamaz. Peki bu ne anlama geliyor? Yukarıdaki kodlara ¸söyle bir ekleme yaparak durumu birazcık da olsa somutla¸stıralım:
def deneme():
sayı = 5 print(sayı) print(sayı)
Bu kod parçasını çalı¸stırdı˘gımızda Python bize ¸söyle bir hata mesajı gösterecektir:
NameError: name ’sayı’ is not defined Yani:
˙IsimHatası: "sayı" ismi tanımlanmamı¸s
Halbuki biz “sayı” adlı de˘gi¸skeni deneme() adlı fonksiyonun içinde tanımlamı¸stık, de˘gil mi?
Evet, biz bu de˘gi¸skeni “deneme” adlı fonksiyon içinde tanımlamı¸stık. Ama önümüzde ¸söyle bir gerçek var: Bu bölümün ba¸sında da söyledi˘gimiz gibi, Python’daki nesnelerin bir kapsama alanı vardır. Yani bu nesneler bir isim alanı içinde yer alır. Dolayısıyla “sayı” adlı de˘gi¸skenin kapsamı deneme() adlı fonksiyonun isim alanı ile sınırlıdır. Yani bu de˘gi¸sken deneme() adlı fonksiyonun dı¸sında var olamaz... Bu “deneme” fonksiyonunun kapsama alanı da fonksiyonun içi ile sınırlıdır.
¸
Simdi ¸suna bir bakalım:
def deneme():
sayı = 5
print("deneme() fonksiyonu içindeki sayı: ", sayı) sayı = 10
print("deneme() fonksiyonu dı¸sındaki sayı: ", sayı) deneme()
Gördü˘günüz gibi, “sayı” adlı de˘gi¸skeni fonksiyon dı¸sında da kullanabilmek için, bu de˘gi¸skeni fonksiyonun dı¸sında da tanımlamamız gerekiyor. Yukarıda iki farklı de˘gerle gösterilen “sayı”
adlı de˘gi¸skenler farklı isim alanları içinde yer aldı˘gı için, aynı ada sahip olmalarına ra˘gmen birbirlerine karı¸smıyorlar. Aslında “isim alanı” kavramı çok güzel bir özelliktir. Büyük program-lar yazarken isim alanı kavramının çok i¸sinize yaradı˘gını göreceksiniz. Bu kavram sayesinde de˘gi¸skenlerin birbirine karı¸smasını önleyebiliyoruz. Hele bir de aynı program üzerinde farklı ki¸siler çalı¸sıyorsa, bu “isim alanı” özelli˘gi hayat kurtarıcı olabilir...
Ancak bazı durumlarda bir isim alanı içinde yer alan bir de˘gi¸skene o isim alanı dı¸sından da eri¸sebilmeniz gerekebilir. Mesela ¸söyle bir ¸sey yazmak isteyebilirsiniz:
def sor():
sayı1 = int(input("bir sayı: ")) sayı2 = int(input("ba¸ska bir sayı: ")) def topla():
Burada sayı alma ve alınan bu sayıları toplama i¸slemleri için ayrı birer fonksiyon olu¸sturduk.
Yukarıdaki yapıya göre, sor() fonksiyonu içinde geçen “sayı1” ve “sayı2” adlı de˘gi¸skenleri topla() fonksiyonu içinde i¸sleme tabi tutabilmemiz gerekiyor. Ancak burada bir problem var. Python’un yapısı gere˘gince, sor() ve topla() adlı fonksiyonlar farklı isim alanlarına, dolayısıyla da farklı kapsamlara sahip... Bundan ötürü, bu iki fonksiyon içindeki de˘gi¸skenlere dı¸sarıdan eri¸semiyoruz. Zaten yukarıdaki kodları çalı¸stırdı˘gımızda Python bize bununla ilgili bir hata mesajı gösterecektir...
Yukarıdaki sorunu a¸sabilmek için Python bize global adlı bir araç sunuyor... global deyimi yardımıyla, bir isim alanı içindeki de˘gi¸skenlere o isim alanı dı¸sından da eri¸sebiliyoruz. Mesela yukarıdaki örne˘gi ¸söyle yazalım:
def sor():
global sayı1 global sayı2
sayı1 = int(input("bir sayı: ")) sayı2 = int(input("ba¸ska bir sayı: ")) def topla():
Bu kodların ba¸sına ekledi˘gimiz “global sayı1” ve “global sayı2” ifadeleri sayesinde “sayı1” ve
“sayı2” adlı de˘gi¸skenlere fonksiyon dı¸sından da eri¸silebilmesini sa˘glıyoruz. Yukarıdaki kodları çalı¸stırdı˘gımızda artık programımız hatasız bir ¸sekilde görevini yerine getirecektir. Sayı dı¸sında bir de˘ger girilmesi durumunda dahi programımızın çökmemesi içintry... except... blok-larından yararlandı˘gımıza dikkat ediyoruz...
Bu arada e˘ger istersek yukarıdaki “global sayı1” ve “global sayı2” ifadelerini birle¸stirebiliriz:
def sor():
global sayı1, sayı2 ...
...
Gelin isterseniz tanıdık bir program üzerinde uygulayalım ö˘grendiklerimizi...
def sor():
global sayı1 global sayı2
sayı1 = int(input("bir sayı: ")) sayı2 = int(input("ba¸ska bir sayı: ")) def topla():
print(sayı1 + sayı2) def çıkar():
print(sayı1 - sayı2) def çarp():
print(sayı1 * sayı2)
seçenek = input("Yapmak istedi˘giniz i¸slem: ") if seçenek == "5":
Gördü˘günüz gibi,global deyimi epey i¸se yarıyor. Ancak size bu deyimle ilgili kötü bir haberim var. Bu deyim ne kadar faydalıymı¸s gibi görünse de aslında ço˘gu zaman i¸sleri karı¸stırabiliyor.
¸
Söyle bir örnek verelim:
a = 5
def de˘gi¸skeni_de˘gi¸stir():
a = 10
print("bu de˘gi¸sken fonksiyon içinde: ", a)
de˘gi¸skeni_de˘gi¸stir()
print("bu de˘gi¸sken fonksiyon dı¸sında: ", a)
Bu kodları çalı¸stırdı˘gımızda ¸söyle bir çıktı alıyoruz:
bu de˘gi¸sken fonksiyon içinde: 10 bu de˘gi¸sken fonksiyon dı¸sında: 5 Bir de ¸suna bakalım:
a = 5
def de˘gi¸skeni_de˘gi¸stir():
global a
a = 10
print("bu de˘gi¸sken fonksiyon içinde: ", a)
de˘gi¸skeni_de˘gi¸stir()
print("bu de˘gi¸sken fonksiyon dı¸sında: ", a)
Bu kodlar ise bize ¸söyle bir çıktı veriyor:
bu de˘gi¸sken fonksiyon içinde: 10 bu de˘gi¸sken fonksiyon dı¸sında: 10
Gördü˘günüz gibi, fonksiyon içindeki i¸slem fonksiyonun dı¸s bölgesini de etkiledi ve normalde de˘geri “5” olması gereken “a” adlı de˘gi¸skeni de˘gi¸sikli˘ge u˘grattı. global deyimi bu özelli˘ gin-den ötürü hiç beklenmedik bir anda programınızı allak bullak edebilir. Özellikle büyük boyutlu ve birden fazla ki¸sinin üzerinde çalı¸stı˘gı programlardaglobal deyimi züccaciye dükkanına fil girmi¸s gibi bir etki yaratabilir... O yüzden Python programcıları genellikleglobal deyimini kul-lanmaktan kaçınır. Hatta Python camiasında ¸söyle bir söz vardır: “E˘ger yazdı˘gınız programda global deyimini kullanmanız gerekti˘gini dü¸sünüyorsanız, programınızı gözden geçirmenizin zamanı gelmi¸s demektir!”
Python’da global yerine benimseyebilece˘gimiz daha sa˘glıklı yollar vardır. Mesela sınıflı yapıları kullanmak gibi... Hiç endi¸se etmeyin. Yeri geldi˘ginde sınıflı yapıları da enine boyuna inceleyece˘gimizden emin olabilirsiniz. Pekiglobal deyimini hangi durumlarda kullanmamızın bir sakıncası yoktur? Esasında, dedi˘gimiz gibi, en iyisi ki¸sinin kendini global deyimine hiç alı¸stırmamasıdır. Ama e˘ger u˘gra¸stı˘gınız kod ufak boyutlu bir ¸seyse ve sizden ba¸ska kimsenin bu kodlar üzerinde çalı¸smayaca˘gından eminseniz, o anda kar¸sıla¸stı˘gınız bir sorunu çözmek için global deyimine ba¸svurmayı tercih edebilirsiniz... Elbette global deyimini kullanmak günah de˘gildir! Sadece, bunu kullanmak iyi bir programcılık tekni˘gi olarak kabul edilmez...