Değişken Deyince Ne Anlamalı?

Bu içerik, kendisine çeviri talebinde bulunduğum ve mailimi oldukça nazik şekilde yanıtlayan Ned Batchelder'in sitesinde paylaştığı yazıdan çevrilmiş ve tarafımca bazı eklemeler yapılmıştır. Yazının orjinal haline buradan ulaşabilirsiniz.


İkinci çeviri yazım ile karşınızdayım. Yazının orjinali, Ned Batchelder tarafından PyCon 2015 için hazırlanmış bir slaytın, yine kendisi tarafından yazıya aktarılmış halidir. Dile yeni başlayan insanların kafasını karıştıran bir nokta olduğunu düşündüğümden dolayı yazıyı çevirmek istedim.


Python'daki name ve value kavramlarından bolca bahsedeceğiz. Bunların karşılıklarını sırasıyla isim ve değer olarak kullanacağız. Literatürdeki karşılıklarını anlayabilmek için bunu belirtmek istedim. Öyleyse başlayalım.

NOT: Yazının orjinaline bağlı kalarak "isim" kavramını çokca kullanacağız. Ancak referans, isim ve değişken kavramları aynı şeyi ifade eder. Kafa karışıklığına mahal bırakmamış olalım.


Python’daki isimlerin ve değerlerin davranışı, özellikle başka diller ile uğraşmış olanlar için bir miktar kafa karıştırıcı olabilir. Aslında temelinde çok basit bir mantık yatıyor. Gelin kafamızı kurcalayan şeyleri netleştirelim!

İsimler ve Değerler

Basitçe başlayalım:

İsimler, değerlere referans gösterirler.

Birçok programlama dilinde olduğu gibi, atama (assigment) ifadesi, sol taraftaki sembolik ismi, sağ taraftaki değer ile ilişkilendirir. Yani Python’da isimlerin değerlere referans gösterdiğini veya ismin bir değer için referans olduğunu söyleriz.

x = 23

Artık x ismi, 23 değerinin referansıdır. x ismini kullandığımızda 23 değerini elde ederiz:

print(x+2) # 25 yazdırır

C dili konusunda deneyimliyseniz, referans kavramını işaretçi (pointer) olarak düşünmek isteyebilirsiniz, ancak bu sizin için bir şey ifade etmiyorsa endişe etmeyin. Neler olduğunu anlamanıza yardımcı olması için diyagramlar kullanacağız. Gri etiket, ok ile işaret edilen değerin referansı olan ismi temsil eder. Örnekte x ismi 23 sayısına referans gösterir:

001-degisken-deyince-ne-anlamali.png

Bu yazıda kodlarımızı diyagramlar ile görselleştireceğiz. Siz de kendi kodlarınızı diyagram şeması ile takip edebilmek için pythontutor.com sitesini kullanabilirsiniz.

Bir değere birden fazla referans tanımlanabilir.

Bir değerin referans olarak tek bir isme sahip olabileceğini düşünmek hatalıdır. Atama ifadesi, başka isimlerin de aynı değere referans göstermelerini sağlayabilir:

x = 23
y = x

Artık x ve y aynı değere işaret ederler:

002-degisken-deyince-ne-anlamali.png

Belirtmekte fayda var. Ne x ne de y isimlerinden birisi gerçek diğeri ise ikinci isim değildir. İkiside değer için aynı öneme sahiptir. Zaten referans gösterme şekilleri de tamamen aynıdır.

İsimlerin atamaları, diğer isimlerden bağımsız olarak yapılır.

İki isim aynı değere işaret ediyorsa, bu iki ismi birbirine bağlamaz. Kısaca, birine yeniden atama yapmak, diğerinin de yeniden atanmasına neden olmaz:

x = 23
y = x
x = 12

003-degisken-deyince-ne-anlamali.png

y = x dediğimizde, sonsuza dek eşit olacakları anlamına gelmez. x‘in yeniden atanması, değerin referansı olarak y‘yi yalnız bırakır. Her zaman eşit olsalardı yaşanacak kaosu hayal edebilirsiniz.

Değerler, referansları olduğu sürece yaşarlar.

Staja veya işe girerken de böyle değil midir? Öhöm, öhöm. Goy goyu bırakıp yazımıza geri dönelim :)

Python, her bir değerin kaç tane referansa sahip olduğunu takip eder ve referansı olmayanları otomatik olarak temizler. Buna garbage collection denir ve artık ihtiyaç kalmadığında değerlerin kendiliğinden yok olduğu anlamına gelir. Bu temizleme işlemine reclaiming de denir.

Küçük Bir Dokunuş

Aşağıdaki kodları konsoldaki Python yorumlayıcısında çalıştırmak ve script olarak çalıştırmak farklı sonuçlar üretir. Ancak scriptte verdiği sonuçları göz önüne alacağız. Ne de olsa bizim için önemli olan script, değil mi? :)


Python'un yerleşik id() fonksiyonunu daha önce duymayanlar olabilir, kısaca değinelim. Argüman olarak verdiğimiz nesnenin kimliğini, yani bellekteki adresini döndürür. Python'daki her şeyin nesne olduğunu, dolayısıyla da bir kimliğe sahip olduğunu unutmayın.


Oluşturduğumuz her değişken, bahsettiğimiz gibi değerimizin bellekte bulunduğu konumu tutar. Ve program çalıştığı sürece sabittir ve diğer nesnelerin kimliğinden benzersizdir. Bellekte tutulma biçimini düşünürken her bir değerin yalnızca tek bir adreste tutulacağını düşünmüş olabilirsiniz ancak birden fazla konumda aynı değer bulunabilir:

a = 3000
b = 3000
c = 3000

print(id(3000)) # 140056999120560
print(id(a))    # 140056999120528
print(id(b))    # 140056999120368
print(id(c))    # 140056999120336

Tüm isimler farklı nesneye referans gösterir. Ancak önceki örneklerde gösterdiğimiz gibi, ismimimizi yine bir isme atarsak aynı konumdaki değeri gösterirler:

a = 3000
b = a
c = a

print(id(3000)) # 140056999120560
print(id(a))    # 140056999120528
print(id(b))    # 140056999120528
print(id(c))    # 140056999120528

Bunu şu şekilde düşünelim a 3000 değerine referans gösterir (a->3000). b ve c isimleri a‘ya, a ismi de 3000 değerini işaret ettiğinden b ve c isimleri de aslında 3000 değerine referans gösterir (b->a->3000, c->a->3000).

Python’da mutable değere sahip bir isim oluşturulduğunda, bellekte o değer için her atamada farklı nesne oluşturulur. immutable değerlerde ise hep aynı nesneyi kullanır.

Önce mutable bir değer üzerinde görelim:

# boştayken kimliğine baktığımızda ve birden fazla kez
# yazdırdığımızda aynı kimliği alırız.
print(id([1,2,3])) # 139947867412032
print(id([1,2,3])) # 139947867412032

# değere referans olarak bir isim atarsak, değerin bellek 
# adresini saklar. dolayısıyla kimlik aynı olur. ardından
# aynı değerde yeni bir değişken tanımlarsak bellekte
# yeni nesne oluşturulur. bu değerin referansı henüz yoktur.
b = [1,2,3]
print(id(b))       # 139947867412032
print(id([1,2,3])) # 139947866044672
print(id([1,2,3])) # 139947866044672

# henüz referansı olmayan nesneye `c` isimli bir referans
# atarız. kabaca atamak istediğimiz değerin bellekte
# referanssız hali varsa o konuma referans atanır, yoksa
# yeni nesne oluşturulur.
c = [1,2,3]
print(id(c))       # 139947866044672
print(id([1,2,3])) # 139947866044099
print(id([1,2,3])) # 139947866044099

# (iki kez çalıştırma sebebimiz id([1,2,3]) çağırdığımızda
# kimliğin değişmediğini göstermektir)

immutable değerlerde denersek yeni nesne oluşturulmadığını görürüz:

print(id((1,2,3,4,5))) # 140170956530688
print(id((1,2,3,4,5))) # 140170956530688

b = (1,2,3,4,5)
print(id(b))           # 140170956530688
print(id((1,2,3,4,5))) # 140170956530688
print(id((1,2,3,4,5))) # 140170956530688

c = (1,2,3,4,5)
print(id(c))           # 140170956530688
print(id((1,2,3,4,5))) # 140170956530688
print(id((1,2,3,4,5))) # 140170956530688

Zaten bir düşününce değeri değiştirilmeyen bir şey için birden fazla yer işgal etmenin bir anlamı olmazdı.

Atama

Atama ile ilgili önemli bir noktanın üzerinde duralım:

Atama işleminde veri kopyalanmaz.

Değerler birden fazla isme sahipse kafanızın karışması ve iki isme ve iki değere sahip olduğumuzu düşünmeniz olasıdır:

x = 23
y = x
# "Şuan iki değerim var: x ve y!" diye düşünebilirsiniz.
# Ancak yanılırsınız: iki isminiz ve sadece bir değeriniz var.

Bir isme değer atamak veriyi kopyalamaz veya yeni bir değer oluşturmaz. Atama işlemi, soldaki ismin sağdaki değere referans göstermesini sağlar. Bu durumda, elimizde sadece bir adet 23 değeri bulunur. x ve y isimleri de bu değere referans gösterir.

Listeler gibi biraz daha komplike değerlere sahip olduğumuzda işler daha ilginç hale gelir:

nums = [1, 2, 3]

004-degisken-deyince-ne-anlamali.png

nums‘ı tanımladıktan sonra başka bir isme atarsak, aynı listeye referans gösteren iki tane isme sahibiz demektir:

nums = [1, 2, 3]
tri = nums

005-degisken-deyince-ne-anlamali.png

Unutmayın: atama kesinlikle yeni değerler oluşturmaz veya veriyi kopyalamaz. Son örnekteki atama ifadesi, listeyi çiftlemez.

Yazdığımız son kod parçasıyla birlikte, elimizde iki isim ile referans gösterilen bir liste olmuş olur. Ve bu büyük bir şaşkınlığa yol açabilecek bir duruma sebebiyet verir: değiştirilebilirlik.

Değerler türlerine göre iki kategoriye ayrılır: mutable (değiştirilebilir) veya immutable (değiştirilemez). immutable değerler sayılar, string’ler ve tuple’lardır. Diğer hemen hemen her nesne (list, dict, kullanıcıların tanımladığı nesneler, ..) mutable‘dır.

  • mutable, değerin yerinde değiştirebilen (in-place) metodlara sahip olduğu anlamına gelir.
  • immutable, değerin hiçbir zaman değiştirilemeyeceği anlamına gelir, değiştirdiğinizi düşündürecek bir şey yaptığınızda, aslında eski değerlerden yeni değerler yaratıyorsunuz demektir.

Sayılar immutable olduğunda, yerinde değişiklik yapamazsınız, yalnızca yeni bir değer oluşturup aynı isme yeniden atayabilirsiniz:

x = 1
x = x + 1

Burada, x+1 tamamen yeni bir değer hesaplar ve sonrasında bu değer x‘e atanır. mutable bir değeri ise genellikle değerin metodlarıyla değiştirebilirsiniz:

nums = [1, 2, 3]
nums.append(4)

Öncelikle listemize bir isim atayalım:

006-degisken-deyince-ne-anlamali.png

Ardından bu listeye başka bir değer ekleyelim:

007-degisken-deyince-ne-anlamali.png

nums‘ın referans gösterdiği değeri değiştirmedik. Başlangıçta, nums ismi üç elemanlı bir listeyi ifade eder. Sonrasında listeye erişmek için aynı ismi kullanmaya devam ederiz, çünkü başka atama yapmadığımızdan dolayı aynı listeye referans göstermeye devam eder. .append metodu, 4 değerini ekleyerek listeyi değiştirir, ancak liste hala aynı listedir ve nums hala onun referansıdır.

x = x + 1 ifadesinin x ismini değiştirdiğini, nums.append(4) kod parçasının ise nums‘ı değiştirdiğini söyleyebiliriz, ancak bunlar çok farklı değişikliklerdir. Birincisi, x ismini yeni bir değere bağlar, ikincisi ise nums isminin ifade ettiği değeri değiştirir.

İnsanların tam olarak bu noktada kafası karışır: iki isim aynı değeri temsil ediyorsa ve değer değişmiş ise her iki isimde değişikliği görür. Tabi bunun değiştirilebilir nesnelerde geçerli olduğuna dikkat edin, değiştirilemez nesnelerde bu durum zaten yaşanamaz.

nums = [1, 2, 3]
tri = nums
nums.append(4)

print(tri)      # [1, 2, 3, 4]

tri‘nin değeri neden değişti? Cevap, şimdiye kadar öğrendiklerimizde gizlidir. Atama yapmak, değeri kopyalamaz. Bu nedenle tri ismine atama yaptıktan sonra, aynı listeye referans olan iki ismimiz olur:

008-degisken-deyince-ne-anlamali.png

Sonrasında listeyi yerinde değiştiren nums.append(4) metodunu çağırırız. tri bu listeyi temsil ettiğinden, baktığımızda değişikliği görürüz. Bu nedenle ismimiz artık dört elemanlı olan listeyi gösterir:

009-degisken-deyince-ne-anlamali.png

Değiştirilebilirlik, insanların Python isimleri ve değerleriyle ilgili kafasını karıştıran asıl şeydir. Bir değer birden fazla isim tarafından paylaşılır ve birinde değiştirilir ise tüm isimler değişikliği görür. Bunun gerçekleşmesi için ihtiyacımız olanlar ise:

  • Değiştirilebilir bir değer alınır, örneğimizde liste,
  • Değere referans gösteren birden fazla isim oluşturulur,
  • Değerin isimlerinden birinde değişiklik yapılır ve
  • Diğer isimler değişikliği görür.

Bu bir hata değildir, bazı durumlarda bu şekilde çalışmasını isteyebiliriz. Programınızın belirli noktalarında birçok değerin birden fazla ismi bulunabilir ve birinde değer değiştirildiğinde tüm isimlerin değişikliği görmesi kesinlikle faydalıdır. Alternatifi ise, değerleri kopyalayarak atama yapmak olabilir ancak bu programlarınızı dayanılmaz derecede yavaşlatır.

Python’da mutable ve immutable değerlerin atamaları farklı şekilde yapılır düşüncesi yanlıştır.

Bu durum yalnızca mutable değerlerde gerçekleştiğinden, bazı insanlar atamanın mutable değerler için farklı şekilde gerçekleştiğine inanır. Ancak durum böyle değildir.

Tüm atamalar aynı şekilde çalışır: bir isim bir değere referans atanır. Ancak immutable bir değer, referans gösteren kaç isim olursa olsun, yerinde değiştirilemez. Böylece diğer isimler bu değişiklikten asla etkilenmez.

Küçük Bir Dokunuş

Atama ve değişiklik derken neyden bahsettiğimizin üzerinde duralım.

Öncelikle yukarıdaki örneği alarak atama öncesi ve sonrası kimliğini kontrol edelim:

x = 1

# atadığımız değerin kimliğine göz atalım
print(id(x))     # 94543789522240

# sonrasında ise x'e atanacak olan x+1'in kimliğine bakalım
print(id(x+1))   # 94543789522272

# atama işlemimizi yapalım ve x'in kimliğine bakalım
x = x + 1  
print(id(x))     # 94543789522272

# x = 1 yerine koyarsak x + 1'in 2 olacağından ona da bakalım
print(id(2))     # 94543789522272

Göreceğiniz üzere üçü de aynı sonucu verdi. Çünkü aslında burada değerimiz 2 olacaktır. 2 değeri immutable olduğundan dolayı verdiğimiz referansların hepsi aynı bellek adresini işaret ederler.

Şimdi ise bir liste üzerinde değişiklik yapalım ve bellek adreslerini kontrol edelim:

nums = [1, 2, 3]

# işlem yapmadan önce listeyi atadığımız "nums" isminin kimliğine bakalım
print(id(nums))   # 139831039432256

# aynı listeye "nums" ismini kullanarak "tri" isimli yeni bir referans atayalım
tri = nums

# "tri" ismimizin bellekte tuttuğu adrese bakalım
print(id(tri))    # 139831039432256
nums.append(4)

# son olarak "nums" isminin değerini değiştirdikten sonra
# "nums" ve "tri" isimlerinin bellek adreslerini kontrol edelim
print(id(nums))   # 139831039432256

tri ismine nums ismini atadığımız için, ikisinin de bellekte aynı adresi işaret etmesini bekleriz. Sonrasında ATAMA YAPMADAN, listede değişiklik yaptığımızda ise yine aynı konumu gösterdiğini görebiliriz. Basit bir örnekle bunu hayal edelim: “Bir kovam var ve içi yarıya kadar su dolu. Biz geri kalan yarısını doldurursak da su hala aynı konumdadır, kovanın içinde. Ancak içindeki değişmiştir.”

Python Dilinin Çeşitliliği

Daha önce Python’un altında yatan basitlikten bahsetmiştik. Mekanizmaları oldukça basittir, ancak çeşitli şekillerde ortaya çıkarlar.

Referans, sadece isimden ibaret değildir.

Şimdiye kadar kullandığımız tüm örneklerde, referans olarak isimler kullanıldı. Ancak başka şeyler de referans olabilir. Python, her elemanı bir değer için referans tutan bir dizi bileşik veri yapısına sahiptir: liste öğeleri, sözlük anahtarları ve değerleri, nesne öznitelikleri vs. Bunların her biri bir atama ifadesinin sol tarafında kullanılabilir ve isimler için bahsettiğimiz şeyler onlar için de geçerlidir. Atama ifadesinin sol tarafında görülebilecek her şey referanstır ve “isim” dediğimiz her yere “referans” kelimesini rahatlıkla koyabilirsiniz.

Liste diyagramlarımızda, eleman olarak sayıları göstermiştik ancak gerçekte her öğe bir sayıya referanstır. Bu nedenle şu şekilde çizilmelidir:

010-degisken-deyince-ne-anlamali.png

Ancak diyagram karmaşıklaşacağı için görsel bir kısaltma kullandık:

010-degisken-deyince-ne-anlamali.png

mutable değerleri işaret eden liste öğeleriniz varsa, liste elemanlarının yalnızca değerlere referanslar olduğunu unutmamak önemlidir.

İşte diğer bazı atamalar. Soldakilerin her biri, birer referanstır:

my_obj.attr = 23
my_dict[key] = 24
my_list[index] = 25
my_obj.attr[key][index].attr = "etc, etc"

Bunun gibi bir çok örnek verebiliriz. Pek çok Python veri yapısı değerler tutar ve bunların hepsi birer referanstır. İsimlerde bahsettiğimiz tüm kurallar, bu referanslara da tamamen aynı şekilde uygulanır. Örneğin, garbage collector yalnızca isimleri saymaz, bir değerin ne zaman reclaimed olacağına karar vermek için her türlü referansı sayar.

i = x atamasının i ismine yapıldığını, ancak i[0] = x atamasının i değerinin ilk öğesine yapıldığına dikkat edin. Tam olarak neye atandığını ayırt edebilmek önemlidir.

Kodlarımızda kullandığımız çoğu şey, atama ifadeleridir.

Pek çok şeyin referans görevi görebileceği gibi, Python’da atama işlemi olan birçok ifade vardır. Aşağıdaki satırların her biri, x ismine yapılan atamalardır:

X = ...
for X in ...
[... for X in ...]
(... for X in ...)
{... for X in ...}
class X(...):
def X(...):
def fn(X): ... ; fn(12)
with ... as X:
except ... as X:
import X
from ... import X
import ... as X
from ... import ... as X

Bu ifadelerin atama gibi davrandığını söylemiyoruz. Aksine bunların hepsi, birer atamadır: hepsinde x ismi bir değere referans gösterir ve atamalar hakkında söylediklerimizin hepsi geçerlidir.

Bu ifadeler çoğunlukla x‘i ifadenin çağırıldığı yer ile aynı kapsamda tanımlar, elbette list compherension gibi bazı istisnalar hariç. Ancak bunların hepsi gerçek atamalardır ve atama ile ilgili her şey geçerlidir.

Python, fonksiyon argümanlarını ilgili isimlere atayarak gönderir.

Gelin atama çeşitlerinin en ilginç olanını inceleyelim: fonksiyon çağrısı. Bir fonksiyonu tanımladığımızda, parametrelerini şöyle adlandırırız:

def my_func(x, y):
    return x+y

Burada x ve y, my_func fonksiyonunun parametreleridir. my_func‘ı çağırdığımızda, fonksiyonların argümanları olarak kullanılacak değerleri göndeririz. Bu değerler, tıpkı bir atama ifadesi kullanılmış gibi parametre isimlerine atanır:

def my_func(x, y)
    return x+y

print(my_func(8, 9))

Yani my_func fonksiyonunu çağrıldığında, x ismine 8 ve y ismine 9 atanır. Bu atama, bahsettiğimiz basit atama ifadeleriyle tamamen aynı şekilde çalışır. x ve y isimleri, fonksiyon için yereldir. Dolayısıyla fonksiyon değer döndürdüğünde bu isimler kaybolur. Ancak referans gösterdikleri değerlerin başka referansları varsa, değerler yaşamlarına devam ederler.

Diğer tüm atamalarda olduğu gibi, mutable değerler fonksiyonlara aktarıldığında değerdeki değişiklikler tüm referanslarda görülür:

def augment_twice(a_list, val):
    a_list.append(val)
    a_list.append(val)

nums = [1, 2, 3]
augment_twice(nums, 4)
print(nums)  # [1, 2, 3, 4, 4]

Bu kod parçası şaşırtıcı sonuçlar doğurabilir. Adım adım ilerleyerek süreci takip edelim. augment_twice fonksiyonunu çağırdığımızda isimler ve değerler şöyle görünür:

010-degisken-deyince-ne-anlamali.png

Fonksiyondaki yerel isimleri bir çerçeve içine aldık. Fonksiyonu çağırdığımızda, diğer herhangi bir atama ifadesinde olduğu gibi değerler parametre isimlerine atanır. Atamanın hiçbir zaman yeni değerler oluşturmadığını veya herhangi bir veriyi kopyalamadığını unutmayın. Bundan dolayı yerel isim a_list, gönderilen değere yani nums‘a referansta bulunur.

Sonra a_list.append metodunu iki kez çağırarak listeyi değiştiririz:

010-degisken-deyince-ne-anlamali.png

Fonksiyon sona erdiğinde yerel isimler yok edilir. Referansı kalmayan değerler reclaimed duruma gelir, diğerleriyse bellekte kalır:

010-degisken-deyince-ne-anlamali.png

Değiştirmek üzere listeyi fonksiyonumuza gönderdik. Hiçbir değer kopyalanmadı. Bu davranış şaşırtıcı olsa da oldukça önemlidir. Bu sayede nesneleri değiştiren yöntemler yazabiliriz.

Yukarıdaki fonksiyonu yazmanın başka bir yolu da budur, ancak düzgün çalışmaz. Nedenine bakalım:

def augment_twice_bad(a_list, val):
    a_list = a_list + [val, val]

nums = [1, 2, 3]
augment_twice_bad(nums, 4)
print(nums)  # [1, 2, 3, 4, 4]

augment_twice_bad fonksiyonunu çağırdığımız anda, daha önce augment_twice fonksiyonunu çağırdığımızdakiyle aynı görüntüyü elde ederiz:

010-degisken-deyince-ne-anlamali.png

Bir sonraki ifade ise atama işlemidir. Sağ taraftaki ifade yeni bir liste oluşturur ve bu liste daha sonra a_list ismine atanır:

010-degisken-deyince-ne-anlamali.png

Fonksiyon sona erdiğinde, yerel isimler yok edilir ve referansı kalmayan değerler çöpe gönderilir ve başladığımız yere döneriz:

010-degisken-deyince-ne-anlamali.png

Bir değeri yerinde değiştirmek ile bir ismi yeniden tanımlamak arasındaki farkı anlayabilmek gerçekten önemlidir. augment_twice fonksiyonu gönderilen değeri değiştirdi ve böylece fonksiyon değer döndürdükten sonra da değişim görüldü. augment_twice_bad fonksiyonu ise gönderilen değeri yerel bir ismi yeniden tanımlamak için atama kullandı, bu nedenle değişiklikler fonksiyonun dışında görülmedi.

Fonksiyonumuz için başka bir seçenek de yeni bir değer oluşturmak ve değeri döndürmektir:

def augment_twice_good(a_list, val):
    a_list = a_list + [val, val]
    return a_list

nums = [1, 2, 3]
nums = augment_twice_good(nums, 4)
print(nums)         # [1, 2, 3, 4, 4]

Burada, augment_twice_good fonksiyonu içerisinde tamamen yeni bir değer oluştururuz ve döndürdürürüz. Fonksiyonu çağırırken bu değeri korumak için bir atama kullanırız ve istediğimiz etkiyi elde ederiz.

Son fonksiyon, beklenmeyen sonuç elde etme olasılığını en aza indirdiği için belki de en iyisidir. Yerinde bir değeri değiştirmeyip, yeni değerler yaratarak süprizlerden sıyrılır.

Yeniden tanımlama ve değişiklik yapmak arasındaki seçimin mutlak doğru bir cevabı yoktur: hangisini kullanacağınız, ihtiyacınız olan etkiye bağlıdır. Önemli olan, her birinin nasıl davrandığını anlamak, elinizde hangi araçlara sahip olduğunuzu bilmek ve daha sonra o an için en iyi olanı seçmektir.

Dinamik Tip Belirleme

Python’daki isimler ve değerler hakkında bazı ayrıntılardan bahsedelim:

Herhangi bir isim, herhangi bir anda herhangi bir değere referans olabilir.

Python dinamik olarak yazılmıştır, yani isimlerin tipi yoktur. Tanımlanan bir isim başlangıçta bir tamsayıya, ardından bir listeye, sonrasında bir fonksiyona ve daha sonra ise bir modüle referans olabilir. Tabii ki, bu çok kafa karıştırıcı olacaktır. Bu yüzden bunu yapmaktan kaçınmalıyız. Ama Python’un umrunda olmaz.

İsimlerin tipi yoktur, değerlerin kapsamı yoktur.

İsimlerin tipi olmadığı gibi, değerlerin de kapsamı yoktur. Bir fonksiyonun yerel değişkeni olduğunu söylediğimizde, ismin kapsamının fonksiyona göre ayarlandığını kastederiz; fonksiyonun dışında ismi kullanamazsınız ve fonksiyon değer döndürdüğünde isim yok edilir. Ancak gördüğümüz gibi değerin başka referansları varsa, fonksiyon çağrısının ötesinde yaşayacaktır. Yerel olan değer değil isimdir.

Değerler silinemez, yalnızca isimler silinebilir.

Python’un bellek yönetimi davranışının merkezinde yer alır, değerleri silmenin bir yolu yoktur. Belki bir yerlerde del ifadesine den gelmiş olabilirsiniz:

nums = [1, 2, 3]
del nums

Bu, nums‘ın değerini silmez, nums ismini siler. İsim uzayından çıkarılır ve ardından olağan referans sayımı devreye girer: nums isminin referans gösterdiği değerin başka referansı yoksa, değer reclaimed duruma gelir. Ancak başka referansları varsa, o zaman yaşamaya devam eder.

“Python’da değişken yoktur” yargısı yanlıştır.

Bazı insanlar “Python’da değişken yoktur, isimler vardır” demeyi severler. Ancak bu slogan yanıltıcıdır. Gerçek şu ki, Python’da değişkenler vardır fakat C’deki değişkenlerden farklı çalışırlar.

İsimler, Python değişkenleridir: değerlere referans olurlar ve bu değerler programınız süresince değişebilir. Sırf başka bir dilin (önemli olsa da) farklı davranması, Python’u değişken içermeyen dil olarak tanımlamak için iyi bir neden değildir.


Bilginin zekatı paylaşmaktır diyerek yazımızı burada sonlandıralım. Maillerinize her zaman açığım. Olumlu yorumlar motive eder, olumsuz yorumlar ise geliştirir. Bu yüzden olumlu olumsuz tüm maillerinizi bekliyorum.

Selametle.