Ansızın ilginç problemlerle karşılaşmamak için değişkenleri ya da fonksiyonları nerede tanımladığımız önemli. Bazen değişkenleri olmamaları gereken yerlerde tanımlayabiliyor ya da dikkatsizlik eseri built-in gelen fonksiyon ya da değişkenleri farkında olmadan gölgeleyebiliyoruz. Python'un değişkenlerin için uyguladığı kendine has bir faaliyet alanı hiyerarşisi var. Fiyakalı da bir ismi var: LEGB.

LEGB Nedir?

LEGB, değişkenleri belirli erişim seviyelerine göre sıralamak için kullanılan alanların baş harflerinden oluşan bir akronim.

  • L Yerel (local): Lambda ya da fonksiyonlar içinde tanımlanan değişkenler için kullanılan alandır. Değişken global anahtar kelimesi ile tanımlanmadığı sürece, tanımlanan değişken yalnızca ilgili lambda ya da fonksiyon içinden erişilebilir. Tahmin ettiğiniz üzere eğer global anahtar kelimesi kullanılırsa değişken global alana yazılır. Yerel değişkenlere göz atmak için built-in locals() fonksiyonu kullanılabilir.
  • E Kapalı (enclosed): Eğer yerelde tanımlı değilse kapsayan fonksiyon içinde yer alan değer kullanılır.
  • G Global (global): Eğer kapalı alanda da tanımlı değilse global alanda aranır. Modül dosyasının en üst seviyesinde ya da global anahtar kelimesi ile tanımlananan isimler için kullanılan alandır. Global alanda neler olduğuna göz atmak için globals() fonksiyonu kullanılabilir.
  • B Yerleşik (built-in): Eğer global alanda tanımlı değilse son olarak yerleşik alanda arama yapılır. Bizim tanımlamadığımız, Python ile birlikte gelen fonksiyon ya da değişkenlerdir. Örneğin; filter(), len() ya da __name__. Hehangi bir ismin yerleşikler içinde olup olmadığını yine başka bir yerleşik olan __builtin__ ile kontrol edebilirsiniz. Örneğin; 'filter' in dir(__builtin__) gibi bir sorgu yerleşikler içerisinde filter fonksiyonu olduğu için True sonuç döndürecektir.

Eğer yorumlayıcı tüm bu süreçlerin sonunda hala aradığını bulamadıysa bir NameError hatası fırlatacaktır.

Python Scoping LEGB
Yukarıda bahsedilen akış tam olarak böyle gerçekleşiyor.

Örnek: Local ve Global

Önce global alanda tanımlı anonim kullanıcıyı bir fonksiyon vasıtasıyla yerel alanda giriş yaptırıyoruz. Ardından global alanda, giriş yapmış kullanıcıyı belirlediğimizi düşünüp çeşitli tehlikeli, gizli kapaklı işler yapıyoruz. Ancak bir bakıyoruz ki kullanıcı giriş yapamamış. :(

Aşırı gerçekçi simülasyon kodu:


user = "anonymous"


def login():
    print ("- giriş işlemi yapılıyor...")
    user = "umut"
    print("- oturumdaki kullanıcı: {}".format(user))


print("- oturumdaki kullanıcı: {}".format(user))

login()

print("- fonksiyon dışında oturumdaki kullanıcı: {}".format(user))

Örnek: Local, Enclosed ve Global

Yukarıdaki örneği biraz değiştirip local ve global arasına bir de enclosed alanı koyalım ve iç içe fonksiyonlarda nasıl davrandığına bakalım:


token = "GLOBAL"


def outer():
    token = "ENCLOSED"

    def inner():
        token = "LOCAL"
        print("- inner içindeki değer: {}".format(token))

    inner()

    print("- outer içindeki değer: {}".format(token))


outer()

print("- dışarıdaki değer: {}".format(token))

Örnek: Gölgeleme

Webde kullanmak üzere URL oluşturan bir fonksiyon var. Çalıştıktan sonra kullanıcıya metni küçük harfe çevirmek isteyip istemediği soruyor (evet kabul, saçma bir örnek). Kullanıcıdan tercihini built-in input() fonksiyonu ile alıyor. Daha doğrusu alamıyor. Çünkü LEGB sıralaması gereği yerel değerini (yani fonksiyona parametre olarak gelen input) alıyor. Fonksiyonu çalıştırıp soruyu sormak istediğinse ise input artık bir fonksiyon olmadığı için TypeError fırlatıyor.


import re


def slugify(input):
    slug = re.sub(r'[^A-Za-z0-9]+', '-', input).strip('-')

    is_lowered = input("Küçük harf olsun mu (e/h)") == "e"

    if is_lowered:
        slug = slug.lower()

    return slug

slug = slugify("Selam, naber?")
print(slug)

Bu, anlaşılması kolay bir örnek. Ancak bir gölgeleme problemini tespit etmek her zaman bu kadar kolay olmayabilir.