koddla

Yazılımcıları bilgi ile güçlendirir.

Değişken Kapsamı

Python’da değişken oluşturma ve değer atamayı istediğimiz değişken ismini yazarak ve eşitliğin karşısına değeri vererek yapabiliyoruz. Verdiğimiz değerin türünü ayrıca belirtmemize de gerek olmuyor. Peki oluşturduğumuz değişkenlerin kapsamı hakkında neler bilmemiz gerekiyor? Öncelikle yerel değişkenlere bakalım.

Yerel Değişkenler

Bir isim bir işlevin içinde tanımlanırsa, varsayılan olarak yalnızca işlev içinden erişiriz:

def foo():
    a = 5
    print(a) # ok

print(a) #  NameError: name 'a' is not defined

Kontrol akışı yapılarının (koşul ifadeleri, döngüler vb.) kapsam üzerinde bir etkisi yoktur (except hariç), ancak henüz atanmamış bir değişkene erişmeye çalışmak hata verecektir:

ef foo():
    if True: 
        a = 5
    print(a) # ok

b = 3
def bar():
    if False:
        b = 5
    print(b) # UnboundLocalError: local variable 'b' referenced before assignment

Yerel Olmayan Değişkenler için Kapsam

Python 3, nonlocal adında yeni bir anahtar kelime ile gelir. Yerel olmayan anahtar sözcüğü, iç kapsamdaki kapsamı geçersiz kılma imkanı verir (daha fazla bilgi için PEP 3104‘ü okuyabilirsiniz). Bu, en iyi şekilde bir kod örneği ile açıklanabilir. En yaygın örneklerden biri, artabilen bir fonksiyon oluşturmaktır:

def counter():
    num = 0
    def incrementer():
        num += 1
        return num
    return incrementer

Bu kodu çalıştırmayı denerseniz, num değişkenine en içteki fonksiyonda atanmadan önce başvurulduğu için bir UnboundLocalError alırsınız. Karışıma nonlocal’i de ekleyelim:

def counter():
    num = 0
    def incrementer():
        nonlocal num
        num += 1
        return num
    return incrementer

c = counter()
c() # = 1
c() # = 2
c() # = 3

Temel olarak nonlocal, bir dış kapsamdaki değişkenlere atama yapmanıza izin verir, ancak küresel bir kapsama izin vermez. Yani sayaç fonksiyonumuzda nonlocal kullanamazsınız çünkü o zaman global bir kapsama atama yapmaya çalışacaktır. Bunu denediğinizde hemen bir SyntaxError alırsınız. Bunun yerine iç içe geçmiş bir fonksiyonda nonlocal kullanmanız gerekir.

(Burada sunulan işlevselliğin generatorler kullanılarak daha iyi uygulandığını unutmayın).

Global Değişkenlerde Kapsam

Python’da, işlevlerin içindeki değişkenler, yalnızca bir atama deyiminin sol tarafında veya başka bir değer atama olayında görünürlerse yerel olarak kabul edilirler; aksi takdirde böyle bir atama, küresel kapsama kadar çevreleyen işlevlerde aranır. Bu durum atama deyimi hiç çalıştırılmasa bile geçerlidir.

x = 'Hi'
def read_x():
    print(x) # x'e sadece referans verilir, bu nedenle global olduğu varsayılır

read_x() # Hi yazdırır

def read_y():
    print(y) # burada y'ye sadece referans veriliyor, bu nedenle global olduğu varsayılıyor

read_y() # NameError: global isim 'y' tanımlanmamış

def read_y():
    y = 'Hey' # y bir atamada görünür, bu nedenle yereldir
    print(y) # yerel y'yi bulacak

read_y() # prints Hey

def read_x_local_fail():
    if False:
        x = 'Hey' # x bir atamada görünür, bu nedenle yereldir
    print(x) # atanmamış olan local z'yi arayacak ve bulunamayacaktır

read_x_local_fail() # UnboundLocalError: yerel değişken 'x' atamadan önce başvuruldu

Normalde, bir kapsam içindeki bir atama aynı isimdeki tüm dış değişkenleri gölgeleyecektir:

x = 'Merhaba'
def change_local_x():
    x = 'Bye'
    print(x)
change_local_x() # bye yazdırır
print(x) # merhaba yazdırır

Bir ismin global olarak bildirilmesi, kapsamın geri kalanı için isme yapılacak tüm atamaların modülün en üst seviyesinde gerçekleşeceği anlamına gelir:

x = 'Hi'
def change_global_x():
    global x
    x = 'Bye'
    print(x)

change_global_x() # bye yazdırır
print(x) # bye yazdırır


Global anahtar sözcüğü, atamaların programın üst seviyesinde değil, modülün üst seviyesinde gerçekleşeceği anlamına gelir. Diğer modüller yine de modül içindeki değişkenlere normal -noktalı erişime – ihtiyaç duyacaktır.

Özetlemek gerekirse: bir x değişkeninin bir fonksiyon için yerel olup olmadığını bilmek için, fonksiyonun tamamını okumalısınız:

  • global x’i bulduysanız, x global bir değişkendir
  • nonlocal x’i bulduysanız, x çevreleyen bir işleve aittir ve ne yerel ne de küreseldir
  • x = 5 veya for x in range(3) veya başka bir bağlayıcı bulduysanız, x yerel bir değişkendir
  • Aksi takdirde x bazı çevreleyen kapsamlara (fonksiyon kapsamı, global kapsam veya yerleşikler) aittir

Yerel ve Global Kapsam

Yerel ve globalkapsam nedir?

Kodun bir noktasında erişilebilir olan tüm Python değişkenleri ya yerel kapsamdadır ya da küresel kapsamdadır.

Bunun açıklaması, yerel kapsamın geçerli işlevde tanımlanan tüm değişkenleri içermesi ve küresel kapsamın geçerli işlevin dışında tanımlanan değişkenleri içermesidir.

foo = 1 # global
def func():
    bar = 2 # local
    print(foo) # foo değişkenini global kapsamdan yazdırır
    print(bar) # bar değişkenini yerel kapsamdan yazdırır


Hangi değişkenlerin hangi kapsamda olduğu incelenebilir. Yerleşik locals() ve globals() işlevleri tüm kapsamları sözlük olarak döndürür.

foo = 1

def func():
    bar = 2
    print(globals().keys())  # global kapsamdaki tüm değişken adlarını yazdırır
    print(locals().keys())  # yerel kapsamdaki tüm değişken adlarını yazdırır

İsim çakışmalarında ne olur?

foo = 1

def func():
    foo = 2  # yerel kapsamda yeni bir foo değişkeni oluşturur, global foo etkilenmez

    print(foo)  # 2 yazdırır

    # foo global değişkeni değişmeden hala var:
    print(globals()['foo'])  # 1 yazdırır
    print(locals()['foo'])  # 2 yazdırır

Global bir değişkeni değiştirmek için global anahtar sözcüğünü kullanın:

foo = 1

def func():
    global foo
    foo = 2  # bu, yerel bir değişken oluşturmak yerine global foo'yu değiştirir

Kapsam, fonksiyonun tüm gövdesi için tanımlanır!

Bunun anlamı, bir değişkenin hiçbir zaman fonksiyonun bir yarısı için global ve sonrasında yerel olmayacağı ya da tam tersi olacağıdır.

foo = 1

def func():
    # Bu fonksiyonun bir yerel değişkeni -foo- vardır, çünkü aşağıda tanımlanmıştır.
    # Yani, foo bu noktadan itibaren yereldir. Global foo gizlidir.

    print(foo) # yerel foo henüz başlatılmadığı için UnboundLocalError hatasını yükseltir
    foo = 7
    print(foo)

Aynı şekilde, tam tersi de:

foo = 1

def func():
    # Bu fonksiyonda, foo başlangıçtan itibaren global bir değişkendir

    foo = 7 # global foo değiştirildi

    print(foo)  # 7
    print(globals()['foo'])  # 7

    global foo # bu fonksiyon içinde herhangi bir yerde olabilir
    print(foo)  # 7

Fonksiyonlar içinde fonksiyonlar 

İşlevler içinde iç içe geçmiş birçok işlev düzeyi olabilir, ancak herhangi bir işlev içinde o işlev için yalnızca bir yerel kapsam ve genel kapsam vardır. Ara kapsamlar yoktur.

foo = 1

def f1():
    bar = 1

    def f2():
        baz = 2
        # burada, foo global bir değişkendir, baz ise yerel bir değişkendir
        # bar her iki kapsamda da değil
        print(locals().keys())  # ['baz']
        print('bar' in locals())  # False
        print('bar' in globals())  # False

    def f3():
        baz = 3
        print(bar) # f1'deki bar referans alınır, böylece f3'ün (closure) yerel kapsamına girer
        print(locals().keys())  # ['bar', 'baz']
        print('bar' in locals())  # True
        print('bar' in globals())  # False

    def f4():
        bar = 4 # bar'ı f1'in yerel kapsamından gizleyen yeni bir yerel bar
        baz = 4
        print(bar)
        print(locals().keys())  # ['bar', 'baz']
        print('bar' in locals())  # True
        print('bar' in globals())  # False

global vs nonlocal kapsam farkı (sadece Python 3) 

Bu anahtar sözcüklerin her ikisi de geçerli fonksiyonlar için yerel olmayan değişkenlere yazma erişimi sağlamak için kullanılır.

global anahtar sözcüğü, bir ismin global bir değişken olarak ele alınması gerektiğini bildirir.

foo = 0  # global foo

def f1():
    foo = 1 # f1 içinde yeni bir foo yerel
    
    def f2():
        foo = 2 # yeni bir foo local in f2
        
        def f3():
            foo = 3 # yeni bir foo local in f3
            print(foo) # 3
            foo = 30 # yalnızca f3'teki yerel foo'yu değiştirir
        
        def f4():
            global foo
            print(foo) # 0
            foo = 100 # global foo'yu değiştirir

Öte yandan, Python 3’te mevcut olan nonlocal, yerel bir değişkeni çevreleyen bir kapsamdan geçerli işlevin yerel kapsamına alır.

nonlocal ile ilgili Python belgelerinden:

Yerel olmayan deyim, listelenen tanımlayıcıların globaller hariç en yakın kapsama önceden bağlanmış değişkenlere başvurmasına neden olur.

def f1():
    
    def f2():
        foo = 2 # f2 içinde yeni bir foo yerel

        def f3():
            nonlocal foo # foo from f2, which is the nearest enclosing scope
            print(foo) # 2
            foo = 20 # f2'den foo'yu değiştirir!

Atama Gerçekleşmesi

x = 5
x += 7
for x in iterable: pass

Yukarıdaki ifadelerin her biri bir bağlama olayıdır – x, 5 ile gösterilen nesneye bağlanır. Bu ifade bir fonksiyonun içinde yer alırsa, x varsayılan olarak fonksiyon-yerel olacaktır.

İşlevler isimlere bakarken sınıf kapsamını atlar

Sınıflar tanımlama sırasında yerel bir kapsama sahiptir, ancak sınıf içindeki işlevler isimleri ararken bu kapsamı kullanmaz. Lambda‘lar fonksiyon olduğundan ve kavramalar fonksiyon kapsamı kullanılarak uygulandığından, bu durum bazı şaşırtıcı davranışlara yol açabilir.

a = 'global'

class Fred:
    a = 'class' # sınıf kapsamı
    b = (a for i in range(10))  # fonksiyon kapsamı
    c = [a for i in range(10)] # işlev kapsamı
    d = a # sınıf kapsamı
    e = lambda: a # işlev kapsamı
    f = lambda a=a: a # varsayılan bağımsız değişken sınıf kapsamını kullanır
    
    staticmethod # veya @classmethod ya da normal örnek yöntemi
    def g():  # işlev kapsamı
        dönüş a

print(Fred.a) # sınıf
print(next(Fred.b))  # küresel
print(Fred.c[0]) # Python 2'de sınıf, Python 3'te global
print(Fred.d) # sınıf
print(Fred.e()) # global
print(Fred.f()) # sınıf
print(Fred.g()) # global

Bu kapsamın nasıl çalıştığını bilmeyen kullanıcılar b, c ve e’nin sınıf yazdırmasını bekleyebilir.

PEP 227’den:

Sınıf kapsamındaki adlara erişilemez. İsimler en içteki çevreleyen fonksiyon kapsamında çözümlenir. Bir sınıf tanımı iç içe geçmiş kapsamlar zincirinde yer alıyorsa, çözümleme işlemi sınıf tanımlarını atlar.

Python’un adlandırma ve bağlama ile ilgili belgelerinden:

Bir sınıf bloğunda tanımlanan adların kapsamı sınıf bloğuyla sınırlıdır; yöntemlerin kod bloklarına uzanmaz – bu, bir işlev kapsamı kullanılarak uygulandıkları için anlama ve üreteç ifadelerini içerir. Bu, aşağıdakilerin başarısız olacağı anlamına gelir:

class A:
    a = 42
    b = list(a + i for i in range(10))

Bu örnekte, Martijn Pieters tarafından verilen ve bu davranışın daha derinlemesine analizini içeren bu yanıttan referanslar kullanılmıştır.

del komutu ile kapsamdan kaldırma

Bu komutun birbiriyle ilişkili ancak farklı birkaç şekli vardır.

del v 

Eğer v bir değişken ise, del v komutu değişkeni kapsamından çıkarır. Örneğin:

x = 5
print(x) # out: 5
del x
print(x) # NameError: 'f' ismi tanımlanmamış

del’in bir bağlama oluşumu olduğuna dikkat edin, bu da aksi açıkça belirtilmedikçe (yerel olmayan veya global kullanarak) del v’nin v’yi geçerli kapsam için yerel yapacağı anlamına gelir. Eğer v öğesini bir dış kapsamda silmek istiyorsanız, del v deyimiyle aynı kapsamda yerel olmayan v veya global v kullanın.

Aşağıdakilerin tümünde, bir komutun amacı varsayılan bir davranıştır ancak dil tarafından zorlanmaz. Bir sınıf bu niyeti geçersiz kılacak şekilde yazılmış olabilir.

del v.name 

Bu komut v.__delattr__(name) çağrısını tetikler.

Amaç, öznitelik adını kullanılamaz hale getirmektir. Örneğin:

class A:
    pass

a = A()
a.x = 7
print(a.x) # out: 7
del a.x
print(a.x) # hata: AttributeError: 'A' nesnesinin 'x' niteliği yok

del v[item] 

Bu komut v.__delitem__(item) çağrısını tetikler.

Amaç, öğenin v nesnesi tarafından uygulanan eşlemeye ait olmamasıdır:

x = {'a': 1, 'b': 2}
del x['a']
print(x) # out: {'b': 2}
print(x['a']) # hata: KeyError: 'a'

del v[a:b] 

Bu aslında v.__delslice__(a, b) çağrısıdır.

Amaç yukarıda anlatılana benzerdir, ancak dilimlerle – tek bir öğe yerine öğe aralıkları ile- amacımızı gerçekleştiririz. Örneğin:

x = [0, 1, 2, 3, 4]
del x[1:3]
print(x) # out: [0, 3, 4]

Bir yanıt yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Back to top