python ve call by sharing
Bildiğim kadarıyla python’ı diğer dillerden ayıran bir başka özelliği değişkenlerin fonksiyonlara nasıl yollandığı. Klasik “call by reference” veya “call by value” şeklinde de değil.
Aşağıdaki örnek bir fonksiyonumuz var.
def add_to_list(x):
x.append(25)
fonksiyon basit. girdi olarak liste alıp, listeye 25 değerinde bir int ekliyor. Hemen fonksiyonu deneyelim:
>>> y = [15, ]
>>> add_to_list(y)
y’nin global alanda değerinin ne olmasını beklersiniz?
>>> y
[15, 25]
Bu şekilde bakarsak call by reference şeklinde gözüküyor. fonksiyonun içinde değeri değiştirdiğimiz halde genel scope’da baktığımızda da değer değişmiş durumda.
Başka bir deneme yapalım.
>>> def add_to_list(x):
... x = x + [25,]
... print x
...
>>> y = [15]
>>> add_to_list(y)
[15, 25]
>>> y
[15]
Bu sefer baktığımızda fonksiyon scope’u içerisinde x, üzerinde oynanmış değere sahipken, fonksiyon scope’undan çıktığımızda x’ün güncellenmediğini görüyoruz.
Bu fonksiyonu ele alırsak da model, call by value şeklinde gözüküyor?
Python iki modele de sahip değil. Bu durum, call by sharing veya call by object reference şeklinde geçiyor daha çok literatürde.
Python’da her şey — veri tipleri de dahil objedir.
>>> x = 42
>>> id(x)
14822312
Herhangi bir instantiation(örnekleme?) yapmasak da arkaplanda x bir objeden türetilerek oluşturulur. Bu nesnelerin hepsinin bir referansı olur ve referanslar da basit olarak hafızada hangi objenin nerede tutulduğunu tutar.
İlk yazdığımız fonksiyonu bir daha hatırlayalım:
>>> y = [15, ]
>>> def add_to_list(x):
... x.append(25)
...
>>> y
[15, 25]
add_to_list() fonksiyonu çalışmaya başladığında arkaplanda y nesnesinin referansı kopyalanıyor. birden fazla referans aynı nesneyi paylaşıyor.
İki referans da aynı nesneyi paylaştığı için, hangi referanstan olursa olsun bir değişiklik yaptığımız da aynı nesne etkilenecektir.
Peki ikinci fonksiyondaki fark ne? Bakalım.
>>> y = [15, ]
>>> def add_to_list(x):
... x = x + [25,]
...
>>> add_to_list(y)
>>> y
[15]
Bu seferki farkın fonksiyon içinde x’in yeni bir liste nesnesine atanıyor oluşu.
x = [15, ] + [25, ]
şeklinden farkı yok. Farklı bir nesne, farklı bir referans demek, memory’de farklı bir yer demek.
immutable (değiştirelemez) objeler
immutable objeler bir kere yaratıldıktan sonra değiştirelemez objelerdir.
yazının başındaki fonksiyonlarda mutable veri tipi olan listeleri kullandık. immutable bir veri tipi kullanırsak senaryo yine değişiyor.
>>> y = "kazu"
>>> def add_to_string(x):
... x += "makino"
...
>>> y
'kazu'
aynı işi listelerle yaptığımızda farklı olan senaryo string kullandığımızda değişti.
Aynı şekilde; iki referans aynı nesneyi (‘kazu’) paylaşsa da nesne immutable olduğu için değiştirilemedi. string, tuple, integer gibi değerler argüman olarak yollandığında — bu objeler özünde iyi ve immutable oldukları için, scope içine başka bir string nesnesi/referans yeni haliyle yerleştiriliyor.