Python da eval(), exec(), globals(), locals(), compile() Fonksiyonları

Bu bölümde beş farklı fonksiyonu bir arada inceleyeceğiz. Bu fonksiyonları birlikte ele almamızın nedeni bunları birbiriyle yakından bağlantılı olması.

Burada işleyeceğimiz bu beş fonksiyon şunlardan oluşuyor:

  1. eval()
  2. exec()
  3. globals()
  4. locals()
  5. compile()

Ancak bu fonksiyonlardan söz etmeye başlamadan önce Python’daki iki önemli kavramı açıklığa kavuşturmamız gerekiyor: Bu kavramlar şunlar:

  1. ifade
  2. deyim

Öncelikle ‘ifade’ kavramından başlayalım.

İngilizcede expression denen ‘ifadeler’, bir değer üretmek için kullanılan kod parçalarıdır. Karakter dizileri, sayılar, işleçler, öteki veri tipleri, liste üreteçleri, sözlük üreteçleri, küme üreteçleri, fonksiyonlar hep birer ifadedir. Örneğin:

>>> 5

>>> 23 + 4

>>> [i for i in range(10)]

>>> len([1, 2, 3])

İngilizcede statement olarak adlandırılan ‘deyimler’ ise ifadeleri de kapsayan daha geniş bir kavramdır. Buna göre bütün ifadeler aynı zamanda birer deyimdir. Daha doğrusu, ifadelerin bir araya gelmesi ile deyimler oluşturulabilir.

Deyimlere birkaç örnek verelim:

>>> a = 5

>>> if a:
...     print(a)

>>> for i in range(10):
...     print(i)

Python programlama dilinde deyimlerle ifadeleri ayırt etmenin kolay bir yolu da eval() fonksiyonundan yararlanmaktır. Eğer deyim mi yoksa ifade mi olduğundan emin olamadığınız bir şeyi eval() fonksiyonuna parametre olarak verdiğinizde hata almıyorsanız o parametre bir ifadedir. Eğer hata alıyorsanız o parametre bir deyimdir. Çünkü eval() fonksiyonuna parametre olarak yalnızca ifadeler verilebilir.

Birkaç örnek verelim:

>>> eval('a = 5')

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 5
      ^
SyntaxError: invalid syntax

Gördüğünüz gibi, eval() fonksiyonu bize bir hata mesajı verdi. Çünkü a = 5 kodu bir deyimdir. Unutmayın, Python’da bütün değer atama işlemleri birer deyimdir. Dolayısıyla eval() fonksiyonu bu deyimi parametre olarak alamaz.

Bir de şuna bakalım:

>>> eval('5 + 25')

30

Bu defa hata almadık. Çünkü eval() fonksiyonuna, olması gerektiği gibi, parametre olarak bir ifade verdik. Bildiğiniz gibi, 5 + 25 kodu bir ifadedir.

Dediğimiz gibi, eval() fonksiyonu deyimleri parametre olarak alamaz. Ama exec() fonksiyonu alabilir:

>>> exec('a = 5')

Bu şekilde, değeri 5 olan a adlı bir değişken oluşturmuş olduk. İsterseniz kontrol edelim:

>>> print(a)

5

Gördüğünüz gibi, exec() fonksiyonu, mevcut isim alanı içinde a adlı bir değişken oluşturdu. Yalnız elbette mevcut isim alanı içinde yeni değişkenler ve yeni değerler oluştururken dikkatli olmamız gerektiğini biliyorsunuz. Zira mesela yukarıdaki komutu vermeden önce mevcut isim alanında zaten a adlı bir değişken varsa, o değişkenin değeri değişecektir:

>>> a = 20

Elimizde, değeri 20 olan a adlı bir değişken var. Şimdi exec() fonksiyonu yardımıyla a değişkeninin de içinde yer aldığı mevcut isim alanına müdahale ediyoruz:

>>> exec('a = 10')

Böylece a değişkeninin eski değerini silmiş olduk. Kontrol edelim:

>>> print(a)

10

Bu tür durumlarda, exec() ile oluşturduğunuz değişkenleri global isim alanına değil de, farklı bir isim alanına göndermeyi tercih edebilirsiniz. Peki ama bunu nasıl yapacağız?

Python programlama dilinde isim alanları sözlük tipinde bir veridir. Örneğin global isim alanı basit bir sözlükten ibarettir.

Global isim alanını gösteren sözlükte hangi anahtar ve değerlerin olduğunu görmek için globals() adlı bir fonksiyonu kullanabilirsiniz:

>>> globals()

Bu fonksiyonu çalıştırdığımızda şuna benzer bir çıktı alırız:

{'__doc__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__name__': '__main__', '__package__': None, '__builtins__': <module 'builtins'>}

Gördüğünüz gibi, elimizdeki şey gerçekten de bir sözlük. Dolayısıyla bir sözlük ile ne yapabilirsek bu sözlükle de aynı şeyi yapabiliriz…

‘globals’ adlı bu sözlüğün içeriği, o anda global isim alanında bulunan nesnelere göre farklılık gösterecektir. Örneğin:

>>> x = 10

şeklinde 10 değerine sahip bir x nesnesi tanımladıktan sonra globals() fonksiyonunu tekrar çalıştırırsanız global isim alanına bu nesnenin de eklenmiş olduğunu görürsünüz.

Dediğimiz gibi, globals() fonksiyonundan dönen nesne bir sözlüktür. Bu sözlüğe, herhangi bir sözlüğe veri ekler gibi değer de ekleyebilirsiniz:

>>> globals()['z'] = 23

Bu şekilde global isim alanına z adlı bir değişken eklemiş oldunuz:

>>> z

23

Yalnız, Python programlama dili bize bu şekilde global isim alanına nesne ekleme imkanı verse de, biz mecbur değilsek bu yöntemi kullanmaktan kaçınmalıyız. Çünkü bu şekilde sıradışı bir yöntemle değişken tanımladığımız için aslında global isim alanını, nerden geldiğini kestirmenin güç olduğu değerlerle ‘kirletmiş’ oluyoruz.

Bildiğiniz gibi, Python’da global isim alanı dışında bir de lokal isim alanı bulunur. Lokal isim alanlarının, fonksiyonlara (ve ileride göreceğimiz gibi sınıflara) ait bir isim alanı olduğunu biliyorsunuz. İşte bu isim alanlarına ulaşmak için de locals() adlı bir fonksiyondan yararlanacağız:

def fonksiyon(param1, param2):
    x = 10
    print(locals())

fonksiyon(10, 20)

Bu fonksiyonu çalıştırdığınızda şu çıktıyı alacaksınız:

{'param2': 20, 'param1': 10, 'x': 10}

Gördüğünüz gibi, locals() fonksiyonu gerçekten de bize fonksiyon() adlı fonksiyon içindeki lokal değerleri veriyor.

globals() ve locals() fonksiyonlarının ne işe yaradığını incelediğimize göre exec() fonksiyonunu anlatırken kaldığımız yere dönebiliriz.

Ne diyorduk?

Elimizde, değeri 20 olan a adlı bir değişken vardı:

>>> a = 20

exec() fonksiyonu yardımıyla a değişkeninin de içinde yer aldığı mevcut isim alanına müdahale edelim:

>>> exec('a = 3')

Bu şekilde a değişkeninin varolan değerini silmiş olduk:

>>> print(a)

3

Dediğimiz gibi, bu tür durumlarda, exec() ile oluşturduğunuz değişkenleri global isim alanı yerine farklı bir isim alanına göndermeyi tercih etmemiz daha uygun olacaktır. Python’da isim alanlarının basit bir sözlük olduğunu öğrendiğimize göre, exec()ile oluşturduğumuz değişkenleri global isim alanı yerine nasıl farklı bir isim alanına göndereceğimizi görebiliriz.

Önce yeni bir isim alanı oluşturalım:

>>> ia = {}

Şimdi exec() ile oluşturacağımız değerleri bu isim alanına gönderebiliriz:

>>> exec('a = 3', ia)

Böylece global isim alanındaki a değişkeninin değerine dokunmamış olduk:

>>> a

20

Yeni oluşturduğumuz değer ise ia adlı yeni isim alanına gitti:

>>> ia['a']

3

Alıntıdır.