Python’da nesne zihinsel modeli: kimlik, değer, durum ve referans

Bu haftadan itibaren nesne yönelimli programlamaya (object-oriented programming, OOP) geçiyoruz. Python’da programın kullandığı her değer, yani her sayı, metin, liste ve sözlük, bir nesnedir (object). Kendi nesne türlerimizi, yani sınıfları (class), gelecek hafta class deyimiyle tanımlayacağız. Bu hafta değişken adlarının nesnelere nasıl bağlandığını ve aynı nesneye iki farklı addan erişince ne olduğunu göreceğiz. 3. haftada listelerle yaptığımız deneyleri bu kez bir kayıtla yineleyeceğiz. Örnekler bir kırtasiyenin stok programından. Her ürünün bir adı ve depoda kaç tane kaldığını gösteren bir stok sayısı var. Kasada satılan kalem depo ekranında da eksilmeli, yani iki ekran aynı ürün kaydını kullanmalı.

Bu sayfadaki kod hücreleri kendiliğinden çalışmaz. Her hücrede önce çıktıyı tahmin edin, sonra Run Code düğmesine basın. Kodu değiştirip yeniden çalıştırabilir, Start Over ile ilk hâline döndürebilirsiniz.

Bu bölümün kapsamı

NotSınavda sorulur
  • Değişken adının bir nesneye bağlanması
  • Nesnenin kimlik, tür ve değer özellikleri
  • Aynı nesneye iki referans olduğunda değişikliğin iki addan da görülmesi
  • is ile == ayrımı
  • Küçük tam sayı ve string önbelleklemesinden dil kuralı çıkarılamayacağı
  • Nesnenin durumu ve durumun hangi yollarla değiştiği
  • Yerinde değiştirme ile yeniden bağlama ayrımı
  • id() değerinin bellek adresiyle ilişkisi
  • Çöp toplama (garbage collection) ayrıntıları
  • CPython’ın hangi nesneleri yeniden kullandığının ayrıntıları

Bu başlıklar konunun devamıdır. İleride karşınıza çıkar, ama bu derste ezberlemeniz beklenmiyor.

Ad bir nesneye bağlanır

C’de ürün kaydını struct Product { char name[20]; int stock; }; diye tanımlayıp struct Product pencil = {"Kalem", 10}; yazdığınızda pencil bellekte iki alanı olan bir kutuydu. pencil.stock = 8; o kutunun içindeki alanı değiştirirdi. Python’da aynı kaydı 3. haftada gördüğünüz sözlükle tutabilirsiniz:

product = {"name": "Kalem", "stock": 10}

Bu satır önce bir sözlük nesnesi oluşturur, sonra product adını o nesneye bağlar. Kutu benzetmesi tek bir adla çalışırken işe yarar, ama iki ad aynı nesneyi gösterdiği anda yetmez. Bu derste şu modeli kullanacağız:

Değişken adı bir nesneye bağlanır.

Model sayılar için de geçerli. 3. haftada gördüğünüz id() bir nesnenin kimlik numarasını döndürüyordu. Hücrede score adına iki kez değer atanıyor ve her atamadan sonra id(score) yazdırılıyor. C’deki kutu modeli doğru olsaydı iki sayı aynı çıkardı. Python’da aynı mı çıkar?

score = 90 satırı 80 nesnesinin içini 90 yapmaz. score adını, değeri 90 olan başka bir nesneye bağlar. Buna yeniden bağlama denir.

İki farklı sayı. Sayılar her çalıştırmada değişebilir. Önemli olan, ikisinin farklı olması: score adı önce bir nesneyi, sonra başka bir nesneyi gösterdi.

Her nesnenin kimliği, türü ve değeri var

Python’ın resmî belgelerindeki veri modeline (data model) göre her nesnenin bir kimliği (identity), bir türü (type) ve bir değeri (value) vardır. Kimlik nesneyi ötekilerden ayırır ve nesne var olduğu sürece değişmez. Tür, nesneyle hangi işlemleri yapabileceğinizi ve değerinin değişip değişemeyeceğini belirler. Değer nesnenin içeriğidir.

Hücre kalem kaydının üç özelliğini yazdırıyor. Hücreyi iki kez çalıştırın. Üç satırdan hangisi değişebilir?

type(product) türü, id(product) kimliği, product ise değeri gösterir. Her çalıştırmada yeni bir sözlük oluştuğu için kimlik değişebilir. Tür ve değer aynı kalır.

Bir sözlük nesnesinin kimliğini, türünü (dict) ve değerini gösteren diyagram. stock değişince nesnenin durumu değişir.

Nesnenin kimlik, tür ve değeri
Notid() değerini bellek adresi diye ezberlemeyin

Python kodunu çalıştıran programa Python uygulaması (implementation) denir. Bilgisayarınıza kurduğunuz Python, CPython adlı uygulamadır. Bu sayfadaki hücreleri çalıştıran Pyodide de tarayıcıda çalışan bir CPython’dır. PyPy ve MicroPython başka uygulamalardır. CPython’da id() nesnenin bellek adresini verir, ama dil bunu garanti etmez. Biz id() değerini yalnız iki adın aynı nesneyi gösterip göstermediğini görmek için kullanacağız. Bu iş için is daha okunaklıdır. Kimlik numarası yalnız aynı anda var olan nesneleri ayırır: artık kullanılmayan bir nesnenin numarası sonradan başka bir nesneye verilebilir.

İki ad, tek kayıt

C’de bir struct değişkenini başka birine verdiğinizde alanlar kopyalanır: struct Product selected = pencil; satırından sonra iki ayrı kayıt olur. Kasa ekranıyla depo ekranının aynı kaydı kullanması için işaretçi gerekirdi: struct Product *selected = &pencil;.

3. haftada Python’da = işaretinin kopya almadığını, var olan nesneye bir ad daha bağladığını gördünüz. Buna aynı nesneye referans verme (aliasing) dedik. Sözlük de listeyle aynı davranır. Hücrede kasa ekranı selected adıyla iki kalem satıyor. print(product) ne yazar?

selected = product yeni bir sözlük oluşturmaz. product ve selected aynı sözlüğün iki adıdır, bu yüzden selected ile yapılan değişikliği product ile bakınca da görürüz. C’de işaretçinin yaptığı işi Python’da düz bir atama yapar.

Benzerlik paylaşımda biter. C’de *selected = eraser; yazmak, selected’ın gösterdiği pencil kaydının üzerine eraser kaydının alanlarını kopyalar. Python’da selected = {"name": "Silgi", "stock": 4} yazmak ise kaydı değiştirmez, yalnız selected adını yeni bir sözlüğe bağlar. product eski sözlüğü göstermeye devam eder.

ÖnemliSınıflardan oluşturulan nesneler de kopyalanmaz

Gelecek hafta account, customer, order gibi adlar kendi yazdığınız sınıflardan oluşturulan nesneleri gösterecek. other = account satırı da hesabın kopyasını almaz, aynı hesaba bir ad daha bağlar. Hesap bir addan değiştirilirse öteki ad da yeni durumu görür.

Aynı kayıt mı, eşit kayıt mı?

C’de iki işaretçinin aynı kaydı gösterip göstermediğini işaretçileri == ile karşılaştırarak, iki kaydın aynı bilgileri taşıyıp taşımadığını ise alanları tek tek karşılaştırarak anlardınız. Python’da bu iki soru için iki ayrı işleç var. 3. haftada gördüğünüz gibi is iki adın aynı nesneyi gösterip göstermediğini, == iki değerin eşit olup olmadığını sorar.

Hücrede second, first sözlüğünün 3. haftada gördüğümüz .copy() ile alınmış kopyası. third adına ise düz atamayla first veriliyor. Üç satır ne yazar?

İçeriği eşit iki nesne iki ayrı kayıt olabilir. İki adın aynı kaydı gösterip göstermediğini is söyler.

  • first == second → True: iki sözlüğün anahtarları ve değerleri eşit.
  • first is second → False: .copy() yeni bir sözlük oluşturdu.
  • first is third → True: third = first aynı sözlüğe bir ad daha bağladı.

is işlecinin nesne kimliğini, çift eşittirin ise değer eşitliğini sorduğunu gösteren diyagram.

Kimlik ve eşitlik

Küçük sayılarda is neden bazen True verir?

Hücrede aynı değerdeki sayılar iki kez karşılaştırılıyor: önce 5 ile 5, sonra metinden dönüştürülen iki 1000. Üç satır ne yazar?

CPython küçük tam sayılar ve bazı stringler için her seferinde yeni nesne oluşturmaz, var olanı yeniden kullanır. Buna önbellekleme (caching) denir, stringlerde interning adı da kullanılır. Uygulama bunu hız ve bellek kazanmak için yapar. “Aynı değerdeki sayılar aynı nesnedir” diye bir dil kuralı yoktur: CPython, PyPy ya da başka bir uygulama hangi nesneleri yeniden kullanacağına kendisi karar verir.

Hücrede 1000’i metinden dönüştürmemizin de bir nedeni var. x = 1000 ve y = 1000 yazarsanız CPython aynı hücredeki aynı sabiti bir kez oluşturabilir ve x is y True verir. Bu da uygulamanın tercihidir. Dilin koyduğu kural şu: değiştirilemez türlerde Python aynı değer için var olan bir nesneyi yeniden kullanabilir, değiştirilebilir türlerde kullanamaz. Bu yüzden iki ayrı [] her zaman iki ayrı listedir.

ÖnemliSayıları ve metinleri == ile karşılaştırın

Bir is deneyinde True görmeniz, başka bir sayıda ya da başka bir uygulamada da True göreceğiniz anlamına gelmez. Değer sorusunu == ile sorun. is en sık value is None biçiminde, bir değerin None olup olmadığını denetlemek için kullanılır. Programda tek bir None nesnesi olduğu için bu soru kimlikle sorulur.

Fonksiyon kaydın kopyasını değil kendisini alır

C’de bir struct’ı fonksiyona verdiğinizde fonksiyon onun kopyasını alırdı. Satışın çağıran koddaki kayda yansıması için işaretçi verirdiniz: sell(&pencil, 2). 4. haftada Python’da parametre adının, çağıranın verdiği nesneye bağlandığını gördünüz.

Hücredeki sell fonksiyonu sözlüğü işaretçi olmadan alıyor. Fonksiyonun içindeki ad product, dışarıdaki ad pencil. Son satır ne yazar?

Çağrıda product parametresi pencil adının gösterdiği sözlüğe bağlanır, Python sözlüğü kopyalamaz. Fonksiyonun sözlükte yaptığı değişikliği çağıran kod da görür. return için de durum aynı: fonksiyon bir sözlüğü döndürdüğünde çağırana kopyasını değil kendisini verir.

Parametreye yeni değer atamak çağıranın adını değiştirmez

C’de void increase(int n) fonksiyonu çağıranın değişkenini değiştiremezdi. C’de işaretçi de kopyalanarak gider: sell(&pencil, 2) çağrısında fonksiyon adresin bir kopyasını alır. Bu adresle asıl kaydı değiştirebilir, ama parametredeki işaretçiye başka bir adres atamak çağıranı etkilemez.

4. haftadaki add_one deneyini hatırlayın. Aşağıdaki fonksiyon da parametresine yeni bir değer atıyor ve içeride 11 yazdırıyor. Son satır ne yazar?

number + 1 değeri 11 olan bir int nesnesi verir. Atama yalnız fonksiyonun yerel number adını bu nesneye bağlar. value adı hâlâ 10 nesnesini gösterir.

İki fonksiyon farklı iş yaptığı için farklı davrandı. sell sözlüğü yerinde değiştirdi: aynı nesnenin içeriği değişti. increase ise adı yeniden bağladı: yerel ad başka bir nesneyi göstermeye başladı. sell içinde product = {"name": "Silgi", "stock": 0} yazsaydık bu da yalnız yerel adı yeniden bağlardı ve pencil değişmezdi.

Tür ise nesneyle hangi işin yapılabileceğini belirler. 3. haftadan hatırlayın: list, dict ve set değiştirilebilir (mutable), int, float, bool, str ve tuple değiştirilemez (immutable) türlerdir. int nesnesinin içini değiştiren bir işlem olmadığı için int parametreyle yalnız yeniden bağlama yapılabilir. Listeyi gösteren bir adı ise hem yerinde değiştirebilir hem yeniden bağlayabilirsiniz. 4. haftada gördüğünüz gibi aynı += yazımı listeyi yerinde uzatır, int’te adı yeniden bağlar.

Nesnenin durumu

OOP’de bir nesnenin o anki verisine onun durumu (state) deriz. Değiştirilebilir bir nesnenin durumu program çalışırken değişebilir.

{"name": "Kalem", "stock": 8} sözlüğünde stock değeri durumun bir parçasıdır. Stok 8’den 7’ye inerse nesnenin durumu değişir, kimliği ve türü aynı kalır. Stok 0 olunca satış yapılamaz: durum, nesneyle neyin yapılabileceğini belirler.

Not“Değer” Python’ın, “durum” tasarımın terimi

Değer (value) Python veri modelinin her nesne için kullandığı terimdir: [70, 80] listesinin değeri içindeki iki sayıdır. Durum (state) ise OOP’de nesneyi tasarlarken kullandığımız terimdir: nesnenin o anki verisi. Bir banka hesabının bakiyesi onun durumudur, bakiye yetmezse para çekilemez. Değiştirilebilir bir nesnenin değeri değiştiğinde çoğu zaman “nesnenin durumu değişti” deriz. İki terim birbirine yakındır, ama ikisini birbirinin yerine kullanmayacağız.

OOP’de sık sık şu soruyu soracağız:

Bu durumun hangi yollarla değişmesine izin veriyoruz?

Stok örneğinde: stock değerini yalnız satış yapan fonksiyon mu değiştirebilmeli, yoksa programın her yerinden product["stock"] = -100 yazılabilmeli mi?

Davranış neden verinin yanında olmalı?

Az önceki sell fonksiyonuyla, stokta iki kalem varken beş kalem satalım. Son satır ne yazar?

Program hata vermeden çalışır. “Stok eksiye düşemez” kuralını denetleyen tek bir satır bile yok.

5. haftada gördüğümüz raise ile fonksiyona bir denetim ekleyelim. Hücre ne yazar? Satış reddedilirse stok değişir mi?

Denetim, stoğu değiştiren satırdan önce çalışır. Satış reddedilince stok olduğu gibi kalır.

Bu küçük örnekte kural sell fonksiyonunun içinde duruyor. Ama program büyüdükçe product sözlüğünde hangi anahtarların bulunması gerektiğini ve sözlüğü hangi fonksiyonların değiştirdiğini izlemek zorlaşır. Başka bir dosyadaki bir fonksiyon product["stock"] değerini hiç denetlemeden değiştirebilir. C’de bir struct ile onu değiştiren fonksiyonlar da böyle ayrı ayrı dururdu.

Nesnenin verisi onun durumudur. Bu veriyi okuyan ve değiştiren fonksiyonlar da nesnenin davranışıdır. Gelecek hafta göreceğimiz sınıf (class), durumu ve davranışı tek bir ad altında, aynı yapıda toplar.

C ile Python yan yana

Bu haftaki işlerin C’deki ve Python’daki karşılıkları:

İş C’de ya da önceki haftalarda Bu haftaki Python yolu Dikkat
Ürünün adını ve stoğunu tek kayıtta tutmak struct Product pencil = {"Kalem", 10}; product = {"name": "Kalem", "stock": 10}: product adı bir sözlük nesnesine bağlanır Ada yeni değer atamak nesneyi değiştirmez, adı başka bir nesneye bağlar
Kasa ve depo ekranının aynı kaydı kullanması İşaretçi: struct Product *selected = &pencil; selected = product = hiçbir türde kopya almaz. Ayrı bir kayıt için .copy() gerekir. .copy() sığ kopyadır: içteki liste ya da sözlük iki kopyada ortak kalır
İki kaydın aynı kayıt mı, eşit kayıt mı olduğunu anlamak İşaretçileri == ile, alanları tek tek karşılaştırmak is kimliği, == değeri sorar Sayıları ve metinleri is ile karşılaştırmayın
Çağıranın kaydındaki stoğu azaltan satış fonksiyonu Fonksiyona işaretçi vermek: sell(&pencil, 2) sell(product, 2): parametre aynı sözlüğe bağlanır Parametreye yeni değer atamak çağıranın adını değiştirmez
Stoğun eksiye düşmesini önlemek sell içinde if ile denetlemek sell içinde raise ValueError Kodun başka bir yeri yine product["stock"] = -100 yazabilir

Python’da selected = product satırı kasaya ve depoya aynı kaydı verir. C’de aynı satır iki ayrı kayıt oluştururdu, çünkü struct ataması kopyalar. Python’da = paylaşır. Satış fonksiyonu için C’de işaretçi gerekirdi. Python’da gerekmez, çünkü parametre zaten verilen nesneye bağlanır. Eksi stok kuralı iki dilde de yarım kalıyor: sell stoğu denetliyor, ama kayıt programın her yerinden değiştirilebilir. Gelecek hafta veriyi ve onu değiştiren fonksiyonları bir sınıfta toplayacağız. 8. haftada da kuralı sınıfın içinde koruyacağız.

Alıştırma: Yerinde mi, yeniden bağlama mı?

Hücre dört satır yazdırıyor. Önce dört satırı tahmin edin, sonra çalıştırın.

Hangi satır listeyi yerinde değiştirdi, hangisi bir adı yeniden bağladı? Hangi adlardan bakınca aynı listeyi görüyorsunuz?

Alıştırma: Referans diyagramı

first = {"balance": 100}
second = first
third = {"balance": 100}

Üç değişken adını ve kaç ayrı sözlük nesnesi olduğunu gösteren bir diyagram çizin. Ardından second["balance"] += 50 satırından sonra hangi adlarla bakınca 150 görüleceğini açıklayın.

İki sözlük nesnesi var. first ve second aynı sözlüğü, third ise içeriği eşit ikinci sözlüğü gösterir. second["balance"] += 50 ortak sözlüğü yerinde değiştirir: first ve second ile bakınca 150, third ile bakınca 100 görülür.

Alıştırma: Geçersiz durumu engelle

Boşluğa, bakiye çekilmek istenen tutara yetmiyorsa ValueError yükselten iki satır yazın. Denetim bakiyeyi azaltan satırdan önce gelmeli: işlem reddedilirse bakiye değişmemeli. Bakiyenin tamamı çekilebilmeli. Run Code sonucu denetler.

if amount > account["balance"]:
    raise ValueError("Yetersiz bakiye")

Sıra sizde: Prosedürel hesap

account = {
    "owner": "Ada",
    "balance": 500,
}

Veriyi bir sözlükte, işlemleri ayrı fonksiyonlarda tutan bu yaklaşıma prosedürel yaklaşım denir. C’de bir struct ve onu işleyen ayrı fonksiyonlarla çalışmak da aynı yaklaşımdır. Para yatırma, para çekme ve bakiye sorgulama fonksiyonlarını yazın. Yatırılan ve çekilen tutar pozitif olmalı, çekilen tutar bakiyeyi aşmamalı. Bu kurallar bozulursa ValueError yükseltin. Bakiye sorgulama fonksiyonu bakiyeyi döndürsün, sözlüğü değiştirmesin. Başarısız bir işlemden sonra sözlüğün değişmediğini doğrulayın.

Çalışır kod görevi: Üç durumu deneyin: normal para çekme, yetersiz bakiye ve negatif tutar. Bakiyeyi en az üç assert ile denetleyin. 5. haftadaki gibi assert satırı, koşul yanlışsa AssertionError yükseltir: assert account["balance"] == 400. Reddedilen bir işlemi try içinde çağırın, yoksa program assert satırına gelmeden durur:

try:
    withdraw(account, 1000)
except ValueError:
    pass
assert account["balance"] == 400

Sonunda şu soruyu cevaplayın:

Bu üç fonksiyonu ve hesabın verisini tek bir yapıda toplarsak ne kazanırız?

Bu sorunun cevabı bizi gelecek hafta class BankAccount tasarımına götürecek.

Sınav provası

Önce kendi cevabınızı seçin; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu okuyun. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav soruları da bu mantıkla yazılır.

1. score = 80 ve ardından score = 90 yazıldığında ne olur?

  • A. score adlı kutudaki 80 silinir, yerine 90 yazılır
  • B. İki ayrı score değişkeni oluşur
  • C. Hata verir; bir ada iki kez atama yapılamaz
  • D. score adı başka bir nesneye yeniden bağlanır
  • E. 80 değeri bellekten hemen silinir ve score geçersiz olur

2. Python veri modelinde her nesnenin sahip olduğu üç özellik nedir?

  • A. Durum, davranış ve sorumluluk
  • B. Sınıf, metot ve nitelik
  • C. Değer, kopya ve referans
  • D. Ad, adres ve boyut
  • E. Kimlik, tür ve değer

3. Bu derste “değer” ile “durum” terimleri nasıl ayrılır?

  • A. “Durum” nesnenin kimliğidir
  • B. İkisi eş anlamlıdır; yalnız farklı kitaplarda farklı yazılır
  • C. “Değer” veri modelinin, “durum” nesne tasarımının terimidir
  • D. “Değer” çalışma anında, “durum” derleme anında belirlenir
  • E. “Değer” yalnız değiştirilemez, “durum” yalnız değiştirilebilir nesneler için kullanılır

4. Aşağıdaki program ne yazdırır?

def find(products, name):
    for product in products:
        if product["name"] == name:
            return product

stock = [{"name": "Kalem", "stock": 10}, {"name": "Silgi", "stock": 4}]
item = find(stock, "Silgi")
item["stock"] = 0

print(stock[1]["stock"])
print(item is stock[1])
  • A. 0 ve True
  • B. Hata verir; fonksiyonun döndürdüğü sözlük değiştirilemez
  • C. 4 ve False
  • D. 4 ve True
  • E. 0 ve False

5. Aşağıdaki program ne yazdırır?

cart = [{"code": "K1"}, {"code": "S2"}]
wanted = {"code": "S2"}

found = False
for item in cart:
    if item is wanted:
        found = True

print(found)
print(wanted == cart[1])
  • A. True ve False
  • B. False ve True
  • C. False ve False
  • D. Hata verir; sözlükler is ile karşılaştırılamaz
  • E. True ve True

6. is operatörünün önerilen kullanımı hangisidir?

  • A. items is [] biçiminde boşluk denetimi
  • B. first is second biçiminde iki listenin içerik karşılaştırması
  • C. value is None biçiminde None denetimi
  • D. score is 100 biçiminde sayı karşılaştırması
  • E. name is "Ada" biçiminde metin karşılaştırması

7. a = 5 ve b = 5 iken a is b bazı ortamlarda True verir. Bundan hangi sonuç çıkarılabilir?

  • A. a ile b artık tek bir değişkendir
  • B. Bu bir uygulama tercihidir, dil kuralı değildir
  • C. is operatörü sayılarda == ile aynı işi yapar
  • D. Küçük sayılarda == kullanmak hatalıdır
  • E. Python’da aynı değerdeki sayılar her zaman aynı nesnedir

8. Aşağıdaki program neden sorunludur?

product = {"name": "Kalem", "stock": 2}

def sell(product, amount):
    product["stock"] -= amount

sell(product, 5)
print(product)
  • A. Stok hata vermeden -3 olur
  • B. Parametre adı dıştaki product ile aynı olduğu için çakışma olur
  • C. Python stoğun eksiye düşmesine izin vermez, ValueError yükseltir
  • D. sell fonksiyonu sözlüğü değiştiremez; değişiklik kaybolur
  • E. KeyError yükselir

9. Aşağıdaki program ne yazdırır?

product = {"name": "Kalem", "stock": 10}
shelf = [product]
alias = product

def sell(item, amount):
    item["stock"] -= amount

sell(alias, 2)
shelf[0]["stock"] += 5
alias = {"name": "Kalem", "stock": 0}
print(product["stock"])
  • A. 10
  • B. 8
  • C. 0
  • D. 13
  • E. 15

10. Aşağıdaki iki program neden farklı davranır?

# A
def add_tag(tags):
    tags.append("oop")

labels = ["python"]
add_tag(labels)
print(labels)

# B
def add_tag(tags):
    tags = tags + ["oop"]

labels = ["python"]
add_tag(labels)
print(labels)
  • A. A’da liste referansla, B’de kopyalanarak geçirildiği için
  • B. A’da append yeni bir liste oluşturup labels adına bağlar
  • C. A’daki labels global olduğu için fonksiyon onu değiştirebilir
  • D. B’de fonksiyon return içermediği için değişiklik kaybolur
  • E. A listeyi yerinde değiştirir, B yerel adı yeniden bağlar

1. Doğru: D. Atama bir kutunun içeriğini değiştirmez; adın hangi nesneyi gösterdiğini değiştirir. İlk nesnenin içi değiştirilmez.

  • A yanlış: C’deki int score; bir bellek kutusu gibi çalıştığı için bu model akla yatkındır. Ama kutu modeli burada yanıltır; int nesneleri değiştirilemez ve içleri güncellenmez.
  • B yanlış: İki atama iki ayrı nesneyle çalıştığı için iki değişken varmış gibi görünür. Oysa tek bir ad vardır; yalnız bağlandığı nesne değişir.
  • C yanlış: Bir adın bir kez bağlandıktan sonra sabit kaldığı düşünülebilir. Oysa yeniden atama geçerli ve olağandır.
  • E yanlış: Eski değer artık kullanılmadığı için hemen silindiği sanılabilir. Ama score adı 90 nesnesini gösterir ve geçerlidir. Eski nesneye ne olacağı çöp toplamanın işidir.

2. Doğru: E. Tür, nesneyle yapılabilecek işlemleri ve değerin değişip değişemeyeceğini belirler; kimlik nesneyi ötekilerden ayırır.

  • A yanlış: Bunlar OOP tasarım terimleridir; veri modelinin üç özelliği değildir.
  • B yanlış: Bunlar sınıf yazarken kullanılan kavramlardır; veri modelinin her nesne için saydığı özellikler değildir.
  • C yanlış: Kopya ve referans işlem biçimleridir; nesnenin özellikleri değildir.
  • D yanlış: Ad nesnenin parçası değildir, yalnızca nesneye bağlanır; dil de adres ve boyutu her nesnenin özelliği olarak tanımlamaz.

3. Doğru: C. “Değer” Python veri modelinin her nesne için kullandığı terimdir. “Durum” ise OOP’de nesneyi tasarlarken kullanılan terimdir: nesnenin o anki verisi. Değiştirilebilir bir nesnenin değeri değişince çoğu zaman “durumu değişti” denir. İki terim birbirine yakındır, ama ayrı yerlerde kullanılır.

  • A yanlış: Durum da kimlik de nesneyi tanıtan özellikler olduğu için karıştırılabilir. Ama kimlik nesne yaşadığı sürece sabittir; durum değişebilir.
  • B yanlış: Günlük dilde iki sözcük birbirinin yerine kullanıldığı için eş anlamlı sanılabilir. Ama “değer” Python belgelerinin, “durum” nesne tasarımının terimidir.
  • D yanlış: C’deki derleme anı ile çalışma anı ayrımı bu eşleştirmeyi akla getirir. Ama değer de durum da program çalışırken nesnede bulunan veriyi anlatır. İki terim zamanla değil, kullanıldıkları yerle ayrılır.
  • E yanlış: Durum, değişebilen veriyle birlikte anıldığı için böyle bir ayrım sanılabilir. Ama her nesnenin bir değeri vardır. Değiştirilebilir bir listenin değeri de içindeki elemanlardır.

4. Doğru: A. return product listedeki sözlüğün kopyasını değil kendisini döndürür. item ve stock[1] aynı sözlüğü gösterir; item ile yapılan değişiklik listeden bakınca da görülür.

  • B yanlış: Fonksiyondan dönen değerin salt okunur olduğu sanılabilir. Python’da böyle bir kısıt yoktur; dönen sözlük her sözlük gibi değiştirilebilir.
  • C yanlış: Fonksiyonun bulduğu kaydın bir kopyasını döndürdüğü düşünülebilir. Ama return kopya almaz; çağırana aynı nesneye giden bir referans verir.
  • D yanlış: İki adın aynı sözlüğü gösterdiği doğru. Ama aynı sözlük değiştirildiği için listeden bakınca da yeni değer 0 görünür.
  • E yanlış: Stoğun 0 olduğu doğru. Ama değişikliğin listeden görülmesi, item ile stock[1]’in aynı sözlük olmasından gelir. Bu yüzden is de True verir.

5. Doğru: B. wanted ayrı yazılmış yeni bir sözlüktür; içeriği cart[1] ile eşittir ama aynı nesne değildir. is kimliği sorduğu için döngü eşleşme bulmaz. İçerik karşılaştırması için item == wanted yazılmalıdır.

  • A yanlış: is içeriğe, == kimliğe bakıyormuş gibi düşünülürse bu sonuç çıkar. Oysa is aynı nesne mi diye, == eşit değerli mi diye sorar.
  • C yanlış: Döngünün eşleşme bulamaması iki sözlüğün eşit olmadığını düşündürebilir. Ama == anahtarları ve değerleri karşılaştırır; ikisi de aynı olduğu için True verir.
  • D yanlış: is çoğunlukla None ile kullanıldığı için başka değerlerle hata vereceği sanılabilir. is her nesneyle çalışır; burada yalnız yanlış soruyu sorar.
  • E yanlış: İçerikleri aynı olan iki sözlüğün is ile de eşleşeceği sanılabilir. Ama is içeriğe değil kimliğe bakar; wanted listedeki sözlüklerden hiçbiri değildir.

6. Doğru: C. Programda tek bir None nesnesi vardır; bu yüzden bir değerin None olup olmadığı kimlikle sorulur.

  • A yanlış: Her [] yeni bir liste oluşturur; bu karşılaştırma hep False verir.
  • B yanlış: is sözcüğü “eşit mi?” diye okunduğu için içerik karşılaştırması sanılabilir. Ama is iki adın aynı nesneyi gösterip göstermediğini sorar; içeriği eşit iki ayrı liste için False verir. İçerik == ile karşılaştırılır.
  • D yanlış: Sayı eşitliği için == kullanılır.
  • E yanlış: Metin eşitliği için == kullanılır; is sonucu uygulama ayrıntısına bağlı olabilir.

7. Doğru: B. CPython küçük tam sayıları yeniden kullanır. Dil bunu zorunlu tutmaz, başka bir uygulama farklı davranabilir. Değer sorusu için == kullanılmalıdır.

  • A yanlış: İki adın aynı nesneyi göstermesi, adların birleştiği izlenimini verir. Ama a ve b iki ayrı addır. b = 6 yazılırsa a yine 5’i gösterir.
  • C yanlış: İki operatör burada aynı sonucu verdiği için aynı işi yapıyormuş gibi görünür. Ama farklı sorulardır; sonuçların rastlantısal olarak örtüşmesi bunu değiştirmez.
  • D yanlış: is daha kesin bir karşılaştırma gibi göründüğü için tercih edilesi sanılabilir. Ama değerlerin eşitliği == ile denetlenir.
  • E yanlış: Deneyde True görüldüğü için sonucu her sayıya genellemek kolaydır. Ama int("1000") ile oluşturulan iki değer aynı nesne olmayabilir.

8. Doğru: A. Program hata vermeden çalışır ve {'name': 'Kalem', 'stock': -3} yazdırır. Eksik olan, stoğun eksiye düşmesini engelleyen bir denetimdir.

  • B yanlış: Aynı adın iki yerde geçmesi bir çakışma gibi görünür. Ama parametre fonksiyonun yerel adıdır ve çağrıda aynı sözlüğe bağlanır; hata çıkmaz.
  • C yanlış: Stok eksiye düşemeyeceği için Python’ın bunu kendisi engelleyeceği sanılabilir. Ama Python sözlükteki değerin anlamını bilmez. Kuralı denetleyen satırı siz yazmazsanız stok -3 olur.
  • D yanlış: C’de struct fonksiyona kopyalanarak gittiği için değişikliğin kaybolacağı sanılabilir. Ama fonksiyon aynı sözlük nesnesini değiştirir ve değişiklik çağıran taraftan da görülür.
  • E yanlış: Fonksiyon dışarıdaki sözlüğün anahtarlarını göremezmiş gibi düşünülebilir. Ama parametre aynı sözlüğe bağlanır ve sözlükte stock anahtarı var.

9. Doğru: D. alias, shelf[0] ve product aynı sözlüğü gösterir. sell bu sözlüğü yerinde 8’e indirir, += 5 aynı sözlüğü 13’e çıkarır. Son satır yalnız alias adını yeni bir sözlüğe bağlar, product eski sözlüğü göstermeye devam eder.

  • A yanlış: Hem fonksiyonun hem listenin kopya aldığı sanılabilir. Ama ikisi de aynı sözlükle çalışır ve iki değişiklik de görülür.
  • B yanlış: Listeye konan sözlüğün kopyalandığı sanılabilir. Ama [product] listesi aynı sözlüğü taşır. shelf[0] ile yapılan artış da product ile bakınca görülür.
  • C yanlış: Son atamanın sözlüğü değiştirdiği sanılabilir. Ama alias = {...} yalnız alias adını yeni bir sözlüğe bağlar, product etkilenmez.
  • E yanlış: Fonksiyonun sözlüğün kopyasıyla çalıştığı sanılabilir. Ama item parametresi aynı sözlüğe bağlanır, satış da product ile bakınca görülür.

10. Doğru: E. İki programda da tags çağıranın listesine bağlanır. A’daki append bu listeyi yerinde değiştirir; değişiklik labels adından da görülür. B’deki tags + ["oop"] yeni bir liste oluşturur ve atama yalnız fonksiyonun kendi tags adını ona bağlar. A ['python', 'oop'], B ['python'] yazdırır.

  • A yanlış: C’deki değerle ve işaretçiyle geçirme ayrımı bu açıklamayı akla yatkın kılar. Ama iki çağrı da aynı biçimde yapılır ve argüman olarak aynı türde bir liste verilir; fark fonksiyonun içinde yapılan işlemdedir.
  • B yanlış: tags + [...] yeni bir liste verdiği için append()’in de yeni liste oluşturduğu sanılabilir. Ama append() var olan listeyi değiştirir ve None döndürür. labels baştan beri aynı listeyi gösterir.
  • C yanlış: labels modül düzeyinde tanımlandığı için fonksiyonun onu bu yüzden değiştirebildiği sanılabilir. Ama fonksiyon listeye tags parametresiyle ulaşır ve append() aynı listeyi değiştirir. B’deki labels da modül düzeyindedir, ama değişmez.
  • D yanlış: Fonksiyonun sonucu çağırana yalnız return ile ulaşır diye düşünülürse bu şık akla yatar. Ama A’da da return yoktur ve değişiklik görülür. B’de return olsa bile dönen liste labels adına atanmadıkça çağıranın listesi değişmezdi.

Tek sayfa özet

  • Python’da her değer bir nesnedir. Değişken adı nesne değildir, bir nesneye bağlanır.
  • Ada yeni değer atamak nesnenin içini değiştirmez, adı başka bir nesneye bağlar. Buna yeniden bağlama denir. product["stock"] = 8 gibi bir atama ise sözlüğün içini değiştirir.
  • Python veri modelinde her nesnenin kimliği, türü ve değeri vardır.
  • b = a kopya almaz. İki ad aynı nesneyi gösterir ve bir addan yapılan değişikliği öteki de görür. C’de işaretçinin yaptığı işi Python’da düz atama yapar.
  • == eşitlik, is kimlik sorusudur. Eşit içerikli iki nesne iki ayrı nesne olabilir.
  • Küçük tam sayı ve string önbelleklemesi uygulamanın tercihidir. Değer karşılaştırmasını == ile yapın, is’i None denetimi gibi kimlik soruları için kullanın.
  • Fonksiyon çağrılınca parametre adı verilen nesneye bağlanır. Fonksiyon nesneyi yerinde değiştirirse çağıran da görür, parametreyi yeniden bağlarsa çağıranın adı değişmez. Değiştirilemez bir nesneyi (int, str) gösteren parametreyle yalnız yeniden bağlama yapılabilir.
  • OOP’de nesnenin o anki verisine durum deriz. Nesnenin verisini okuyan ve değiştiren fonksiyonlar onun davranışıdır.
  • Sözlükteki veriyi programın her yeri değiştirebilir. Veriyi ve onu kurallara uyarak değiştiren fonksiyonları tek bir yapıda, sınıfta toplamamızın bir nedeni bu.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • Değişken adının bir nesneye bağlandığını, kutu olmadığını açıklar.
  • Bir nesnenin kimlik, tür ve değer özelliklerini ayırt eder.
  • Aynı nesneye iki referans olduğunda bir addan yapılan değişikliğin ötekinden görüldüğünü gösterir.
  • is ile == sorularını ayırt eder ve is işlecini yalnızca kimlik sorusu için kullanır.
  • Küçük tam sayı ve string önbelleklemesinden dil kuralı çıkarılamayacağını açıklar.
  • Nesnenin durumunu tanımlar ve programda durumu değiştiren satırları bulur.
  • Bir nesneyi yerinde değiştirmek ile bir adı başka bir nesneye yeniden bağlamak arasındaki farkı açıklar.
Başa dön