Sınıf, nesne, self, __init__ ve metotlar

Bu bölümde kendi nesne türlerimizi tanımlayacağız. C’de struct ile birkaç alanı tek bir ad altında topluyordunuz. Sınıf (class) da alanları bir araya getirir, ama yanlarına o alanlarla çalışan fonksiyonları da koyar. Alanların o anki değerleri nesnenin durumudur, fonksiyonların yaptığı işler de davranışıdır. Sınıftan oluşturulan her örnek (instance) kendi durumunu taşır. Geçen hafta sözlükte tuttuğumuz kaydın iki sorunu vardı: sözlükte hangi anahtarların bulunması gerektiğini ve sözlüğü hangi fonksiyonların değiştirdiğini izlemek zordu. Sınıf ikisini aynı tanımın içinde toplar. Örneklerin çoğu bir bankanın hesap programından: her hesabın bir sahibi ve bir bakiyesi var.

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
  • Sınıf ile örnek ayrımı ve class ile tür tanımlama
  • __init__ içinde örnek niteliklerini oluşturma
  • self parametresinin, metodu çağrılan nesne olması
  • Aynı sınıftan oluşturulan nesnelerin niteliklerinin birbirinden bağımsız olması
  • Sınıf niteliği, örnek niteliği ve gölgeleme
  • Paylaşılan mutable sınıf niteliğinin riski
  • Niteliği doğrudan atamak yerine bir metotla değiştirmenin gerekçesi
  • @classmethod ve @staticmethod
  • __new__ ve nesne oluşturma ayrıntısı
  • __slots__

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

İlk sınıfımız

C’de önce struct Account türünü tanımlar, sonra o türden bir değişken oluştururdunuz. Python’da yeni bir tür class deyimiyle tanımlanır. Hücrede BankAccount sınıfı tanımlanıyor ve Ada için bir hesap açılıyor. İki satır ne yazar?

BankAccount sınıftır, account ise bu sınıftan oluşturulmuş bir nesneye referanstır. BankAccount("Ada", 500) satırı sınıfı bir fonksiyon gibi çağırır: Python yeni bir nesne oluşturur, __init__ metodunu çalıştırır ve nesneyi döndürür. Sınıfın içine yazılan fonksiyonlara metot denir, __init__ de bir metottur. self.owner ve self.balance nesneye bağlı değişkenlerdir. Bunlara nitelik (attribute) denir, C’deki struct alanlarına benzerler. __init__ yeni nesne oluşturulunca kendiliğinden çalışır ve niteliklere ilk değerlerini verir.

Not__init__ hazır gelen nesneyi başlatır

Python’da nesne önce oluşturulur, sonra başlatılır. Oluşturma adımı __new__ metodunda olur, bu derste ayrıntısına girmiyoruz. __init__ ikinci adımdır: hazır gelen nesneyi self parametresiyle alır ve niteliklerine ilk değerlerini verir. Bu yüzden __init__ için bazen “yapıcı” (constructor) denir, ama nesneyi o yaratmaz.

Sınıftan nesne oluşturulurken __init__ metodunun ve self üzerinden örnek niteliklerinin kurulmasını gösteren akış.

init ve self akışı

self nedir?

Hücrede aynı sınıftan iki hesap açılıyor. __init__ iki kez çalışıyor ve iki kez self.owner = owner satırını işletiyor. Ece’nin hesabı açılınca Ada’nın hesabındaki ad değişir mi? İki satır ne yazar?

self, metodu hangi nesne için çağırdıysanız o nesnedir. first oluşturulurken çalışan __init__ içinde self, first’ün gösterdiği nesnedir. second oluşturulurken self bu kez second’ın gösterdiği nesnedir. Bu yüzden self.owner = owner ataması her seferinde başka bir nesnenin niteliğini doldurur.

C’de bir struct’la çalışan fonksiyona hangi kayıtla çalışacağını işaretçi parametresiyle söylerdiniz: void init_account(struct Account *a, ...). self o parametrenin Python’daki karşılığıdır. Farkı, çağrıda onu sizin yazmamanız: Python nesneyi kendisi gönderir.

Davranışı sınıfa eklemek

Para yatırma işini sınıfa bir metot olarak ekliyoruz. Hücrede Ada’nın hesabına 50 yatırılıyor. Son satır ne yazar?

C’de bu işi deposit(&account, 50) diye yazardınız. Python’da account.deposit(50) yazılır ve Python bu çağrıyı BankAccount.deposit(account, 50) gibi çalıştırır: noktanın solundaki nesne self parametresine gider. Kodda ilk biçimi yazarız, ikincisi self’in nereden geldiğini anlamaya yardım eder.

Bir örnek üzerinden yapılan metot çağrısında self parametresinin çağrılan nesneyi göstermesini gösteren diyagram.

self ve metot çağrısı

Metot, nesnenin kendi verisiyle yaptığı iştir

Metot sınıfın içine yazılmış bir fonksiyondur, ama fonksiyonu hangi sınıfa yazdığınız da bir şey söyler: o iş hangi nesneye ait? deposit() metodunu BankAccount içine koymak, bakiyeyi değiştirmenin hesabın işi olduğunu söyler. Bir sınıfın üstlendiği işlere o sınıfın sorumluluğu denir.

Aynı sınıftan bağımsız nesneler

Ada’nın hesabına yatırılan para Ece’nin hesabını değiştirmemeli. Bunu küçük bir sayaç sınıfıyla deneyelim. first iki kez, second bir kez artırılıyor. Üç satır ne yazar?

__init__ her nesne için ayrı çalışır ve her nesnenin kendi value niteliğini oluşturur. Sınıfı iki kez çağırmak, C’de iki ayrı struct değişkeni tanımlamak gibi iki ayrı nesne verir. 6. haftadaki gibi second = first yazsaydık iki ad aynı sayacı gösterirdi (aliasing: iki adın aynı nesneyi göstermesi) ve first is second True olurdu.

Sınıf niteliği ve örnek niteliği

Bankanın adı bütün hesaplarda aynıdır, okulun adı da bir okuldaki bütün öğrencilerde. Böyle bir bilgiyi her nesneye ayrı ayrı yazmak yerine sınıfa bir kez yazabilirsiniz. Hücredeki Student sınıfında school sınıfın gövdesinde, name ise __init__ içinde atanıyor. ada nesnesine school diye bir nitelik hiç atanmadı. print(ada.school) hata verir mi? Hücre ne yazar?

name, __init__ içinde self.name ile atandığı için her öğrencinin kendisine aittir. Buna örnek niteliği denir. school ise sınıfın gövdesinde, metotların dışında tanımlanmıştır. Buna da sınıf niteliği denir. ada.school yazdığınızda Python önce ada nesnesinde school adlı bir nitelik arar. Bulamazsa sınıfa bakar ve oradaki değeri verir.

Sınıf niteliği ile her nesneye özgü örnek niteliği arasındaki farkı gösteren diyagram.

Sınıf ve örnek niteliği

Nitelik gölgeleme (shadowing)

Hücrede yalnız ada.school niteliğine yeni bir değer atanıyor. Üç satır ne yazar?

ada.school = ... sınıf niteliğini değiştirmez, yalnız ada nesnesinde aynı adlı bir örnek niteliği oluşturur. Artık ada.school okunurken Python bu niteliği bulur ve sınıfa hiç bakmaz. Buna gölgeleme (shadowing) denir: nesnedeki nitelik, sınıftaki aynı adlı değeri o nesne için örter. Sınıftaki değeri gerçekten değiştirmek istiyorsanız bunu açıkça Student.school = ... ile yazın.

Paylaşılan mutable sınıf niteliği

Sınıf niteliği bir liste olursa ne olur? Hücrede yalnız first takımına üye ekleniyor. print(second.members) ne yazar?

members sınıf niteliği olduğu için iki nesne de aynı listeyi görür. first.members.append(...) bir atama değildir, bu yüzden gölgeleme olmaz: sınıftaki tek liste yerinde değişir. Her takımın kendi üye listesi olsun istiyorsak listeyi __init__ içinde oluştururuz:

class Team:
    def __init__(self, name):
        self.name = name
        self.members = []
UyarıMutable sınıf niteliğine dikkat

Bütün örneklerin gerçekten aynı veriyi görmesi gerekiyorsa sınıf niteliği kullanılabilir. Her nesnenin kendi listesi, sözlüğü ya da kümesi gerekiyorsa mutable nesneyi __init__ içinde oluşturun.

Yanlış yazılan nitelik adı hata vermeden yeni nitelik olur

C’de bir struct’ın alanları tanımda sabittir. Olmayan bir alana değer atarsanız derleyici hata verir. Python’da program çalışırken çoğu nesneye yeni nitelik eklenebilir. Hücredeki reset() metodu bakiyeyi sıfırlamak için yazıldı. Son satır ne yazar?

reset() içindeki ad balance değil blance. self.blance = 0 satırı hata vermez, yanlış adla yeni bir nitelik oluşturur ve balance olduğu gibi kalır. Hatayı ancak sonuç yanlış çıkınca fark edersiniz. Bu bir mantık hatasıdır.

UyarıBütün nitelikleri __init__ içinde oluşturun

Nesnede bulunması gereken bütün nitelikleri __init__ içinde atayın, öteki metotlarda yeni nitelik oluşturmayın. Böylece bir nesnenin hangi nitelikleri olduğunu tek bir yerde görürsünüz. Nitelik adını elle yazmak yerine editörün önerdiği tamamlamayı kullanın.

Tanımanız yeterli: @classmethod ve @staticmethod

Bu hafta yazacağınız metotlar örnek metotlarıdır: ilk parametreleri self olur ve account.deposit(50) gibi bir nesneyle çağrılırlar. İnternetteki örneklerde metodun üstüne @ ile yazılmış iki işarete sık rastlayacaksınız. Bu işaretlere dekoratör (decorator) denir:

class User:
    @classmethod
    def from_text(cls, text):
        ...

    @staticmethod
    def is_valid_name(name):
        ...
  • @classmethod ilk parametre olarak nesneyi değil, sınıfın kendisini (cls) alır. Nesneyi başka bir biçimden, örneğin bir metin satırından oluşturan metotlarda kullanılabilir.
  • @staticmethod ne self ne cls alır. Nesnenin verisini kullanmayan, ama sınıfla ilgili olduğu için sınıfın içine yazılan yardımcı fonksiyonlarda kullanılabilir.

Bu ikisi sınavda sorulmaz. Önce nesnenin niteliklerini kullanan örnek metotlarını doğru yazmayı öğreneceğiz.

Sorumluluğu doğru yere koymak

Para çekme kuralı nerede durmalı? Aynı işi iki biçimde yazabilirsiniz:

# A: dışarıdan doğrudan güncelleme
account.balance -= 100
# B: davranış üzerinden güncelleme
account.withdraw(100)

A’da bakiyeyi dışarıdaki kod kendisi azaltır. Bakiye tutara yetiyor mu, bunu denetlemek bakiyeyi değiştiren her kod parçasının işi olur. B’de para çekmenin kuralları withdraw metodunda, yani hesabın içinde durur: para çekmek hesabın sorumluluğudur. C’de de kuralı bir withdraw fonksiyonuna yazabilirdiniz, ama account.balance -= 100 yazan kodu derleyici durdurmazdı. Python da durdurmaz. Gelecek hafta bu kuralları, örneğin bakiyenin eksiye düşmemesini, withdraw içine yazacağız.

C ile Python yan yana

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

İş C’de ya da 6. haftada Bu haftaki Python yolu Dikkat
Sahibin adını ve bakiyeyi bir arada tutan hesap açmak struct Account tanımı ve struct Account a = {"Ada", 500}; ya da bir sözlük class BankAccount ve __init__, sonra BankAccount("Ada", 500) __init__ nesneyi oluşturmaz, hazır gelen nesneye ilk değerlerini verir
Para yatırmak Hesabı işaretçiyle alan fonksiyon: deposit(&a, 50) account.deposit(50): Python hesabı self olarak gönderir Metot tanımında self unutulursa çağrı TypeError verir
Ada’nın ve Ece’nin hesaplarını birbirinden ayrı tutmak İki ayrı struct değişkeni Sınıfı iki kez çağırmak: BankAccount("Ada", 500) ve BankAccount("Ece", 900) ece = ada yeni hesap açmaz, aynı nesneye ikinci bir ad verir
Bütün nesnelerde aynı olan bilgiyi tek yerde tutmak Global değişken ya da #define Sınıf niteliği: sınıf gövdesinde school = "Samsun MYO" Nesne üzerinden atama sınıf niteliğini değiştirmez, gölgeler. Liste gibi mutable değerleri __init__ içinde oluşturun
Para çekme kuralını tek yerde tutmak withdraw fonksiyonu, ama a.balance -= 100 yazan kodu derleyici durdurmaz account.withdraw(100) metodu Python da account.balance -= 100 satırını durdurmaz

İlk üç satırda C’deki struct ve işaretçili fonksiyon, Python’da tek bir sınıf tanımına dönüştü: alanlar nitelik oldu, işaretçi parametresi self oldu. C’de ortak bilgiyi global değişkende tutardınız. Sınıf niteliği bu bilgiyi programın her yerine değil, yalnız o sınıfa bağlar. Para çekmede bir eksik kalıyor: kural withdraw metodunda durabilir, ama niteliği doğrudan değiştiren kodu Python durdurmuyor. Gelecek hafta bunu nasıl önleyeceğimizi göreceğiz.

Alıştırma: Sınıfı tamamla

self.price *= (1 - rate)

Alıştırma: self hatasını bul

Lamp().turn_on() çağrısında ne olur? Önce tahmin edin, sonra bu çağrıyı hücrenin sonuna ekleyip çalıştırın. Ardından metot tanımındaki eksikliği düzeltin.

Alıştırma: Gölgelemeyi gözlemle

Bir Student sınıfında school sınıf niteliği oluşturun. first ve second adında iki öğrenci nesnesi oluşturun, yalnız ilk öğrenciye first.school = "X" atayın. Üç değeri (first.school, second.school, Student.school) yazdırıp sonucu açıklayın.

Sıra sizde: Book sınıfı

Bir Book sınıfı oluşturun. Sınıfın üç niteliği olsun:

  • title
  • author
  • is_borrowed

Başlangıçta is_borrowed = False olsun. borrow() ve return_book() metotlarını ekleyin. İki ayrı kitap nesnesi oluşturun ve birini ödünç almanın ötekini değiştirmediğini gösterin.

Şimdilik aynı kitabın iki kez ödünç alınmasını engellemiyoruz. Bu tür geçersiz durumları gelecek hafta engelleyeceğiz.

Çalışır kod görevi: En az iki Book nesnesi oluşturun. borrow() ve return_book() çağrılarından sonra is_borrowed değerlerini assert ile kontrol edin. Ayrıca bir örnek niteliğinin sınıf niteliğini gölgelediğini gösteren en az bir kısa deney ekleyin.

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. Sınıf ile nesne (örnek) arasındaki fark nedir?

  • A. Sınıf yalnızca veriyi, nesne yalnızca davranışı tutar
  • B. Nesne sınıfın kopyasıdır; sınıf değişirse nesne değişmez
  • C. Her sınıftan yalnızca bir nesne oluşturulabilir
  • D. Sınıf program çalışırken, nesne kod yazılırken var olur
  • E. Sınıf türü tanımlar; nesne o türün bir örneğidir

2. __init__ metodu ne yapar?

  • A. Nesne silinirken çalışır ve kaynakları serbest bırakır
  • B. Örneğin metinsel temsilini döndürür
  • C. Nesneyi yoktan yaratır ve belleği ayırır
  • D. Yeni oluşturulan örneğe ilk nitelik değerlerini verir
  • E. Sınıf niteliklerini tanımlar

3. Aşağıdaki sınıfta reset() çağrıldıktan sonra balance neden hâlâ 100’dür?

class Account:
    def __init__(self):
        self.balance = 100

    def reset(self):
        self.blance = 0
  • A. reset() metodu self almadığı için çalışmaz
  • B. self.blance yeni bir nitelik oluşturur ve balance değişmez
  • C. balance __init__ içinde tanımlandığı için salt okunurdur
  • D. AttributeError yükselir ve atama yapılmaz
  • E. Metot içinde nitelik değiştirmek için global bildirimi gerekir

4. Bir metodun içinde self hangi nesneyi gösterir?

  • A. Metodun çağrıldığı nesneyi
  • B. Metodun dönüş değerini
  • C. Son oluşturulan örneği
  • D. Sınıfın kendisini
  • E. Hiçbirini; self bir anahtar sözcüktür

5. Aşağıdaki sınıfta Lamp().turn_on() çağrısı ne yapar?

class Lamp:
    def __init__(self):
        self.is_on = False

    def turn_on():
        self.is_on = True
  • A. TypeError yükselir; metotta örneği alacak parametre yoktur
  • B. NameError yükselir; self tanımsızdır
  • C. Metot tanımı söz dizimi hatası verir
  • D. Sorunsuz çalışır; self metot içinde kendiliğinden tanımlıdır
  • E. is_on değerini True yapar ama başka örnekleri etkilemez

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

class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1

first = Counter()
second = Counter()
first.increment()
first.increment()
second.increment()

print(first.value, second.value)
  • A. 3 3
  • B. 1 1
  • C. 2 2
  • D. 0 0
  • E. 2 1

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

class Student:
    school = "Samsun MYO"

    def __init__(self, name):
        self.name = name

ada = Student("Ada")
ece = Student("Ece")
ada.school = "Başka MYO"

print(ada.school, ece.school, Student.school)
  • A. AttributeError; sınıf niteliği örnek üzerinden değiştirilemez
  • B. Başka MYO Samsun MYO Samsun MYO
  • C. Başka MYO Başka MYO Samsun MYO
  • D. Üç değer de Samsun MYO
  • E. Üç değer de Başka MYO

8. Student sınıfının gövdesinde school = "Samsun MYO" satırı var ve ada bu sınıfın bir örneği. Bütün örneklerin gördüğü school değerini değiştirmek için hangisi yazılmalıdır?

  • A. global school bildirip atamak
  • B. Student.school = "Yeni MYO"
  • C. Student("Can").school = "Yeni MYO"
  • D. ada.school = "Yeni MYO"
  • E. self.school = "Yeni MYO" (herhangi bir metot içinde)

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

class Team:
    members = []

    def __init__(self, name):
        self.name = name

first = Team("A")
second = Team("B")
first.members.append("Ada")

print(second.members)
  • A. ['Ada', 'Ada']
  • B. []
  • C. None
  • D. ['Ada']
  • E. AttributeError

10. Her takımın bağımsız bir üye listesine sahip olması için sınıf nasıl yazılmalıdır?

  • A. __init__ içinde self.members.clear() çağrılmalıdır
  • B. members = [] satırı __init__ tanımının altına taşınmalıdır
  • C. Liste __init__ içinde self.members = [] ile oluşturulmalıdır
  • D. Sınıf gövdesinde members = [] yerine self.members = [] yazılmalıdır
  • E. Sınıf gövdesindeki members = [] satırı members = list() yapılmalıdır

11. account.balance -= 100 yerine account.withdraw(100) yazmanın tasarım gerekçesi nedir?

  • A. Python’da nitelikler dışarıdan değiştirilemez
  • B. Doğrudan atama söz dizimi hatası verir
  • C. Para çekme kuralı hesabın metodunda durur
  • D. Metot çağrısı doğrudan atamadan daha hızlı çalışır
  • E. Metot kullanmak nitelik adını gizler

1. Doğru: E. Sınıf, nesnelerin hangi nitelikleri ve metotları olacağını tanımlar. Nesne bu tanımdan oluşturulur ve kendi nitelik değerlerini taşır.

  • A yanlış: Nitelik ile metot ayrımı, sınıf ile nesne ayrımına yanlış eşlenebilir. Oysa sınıf hem nesnenin hangi nitelikleri taşıyacağını hem metotlarını tanımlar.
  • B yanlış: Nesnenin sınıftan “basıldığı” benzetmesi kopya fikrini doğurur. Ama örnek sınıfın kopyası değildir; metotlarını sınıfta bulur.
  • C yanlış: İlk örneklerde her sınıftan tek nesne oluşturulduğu için böyle bir sınır sanılabilir. Oysa bir sınıftan istediğiniz kadar nesne oluşturabilirsiniz.
  • D yanlış: İkisinin farklı zamanlarda var olduğu düşüncesi C’deki derleme anı ile çalışma anı ayrımından gelir. Oysa ikisi de program çalışırken vardır; class satırı da çalıştırılan bir deyimdir.

2. Doğru: D. Python’da nesne önce oluşturulur, sonra başlatılır. __init__ ikinci adımdır; self ile örneğin niteliklerine ilk değerlerini verir.

  • A yanlış: Nesnenin ömrünün iki ucu, başlangıç ve bitiş, kolayca karıştırılır. Bu, yıkıcının (destructor) davranışıdır; __init__ ise nesne oluşturulduğunda çalışır.
  • B yanlış: Çift alt çizgili metotların adları birbirine benzediği için görevleri karışabilir. Bu __str__ ve __repr__ metotlarının işidir.
  • C yanlış: “Yapıcı” adı nesneyi bu metodun kurduğu izlenimini verir. Ama nesneyi oluşturmak ayrı bir adımdır; __init__ hazır gelen nesneyi başlatır.
  • E yanlış: __init__ sınıfın içinde yazıldığı için sınıf düzeyinde iş yaptığı sanılabilir. Ama sınıf nitelikleri sınıf gövdesinde tanımlanır; __init__ örneğin niteliklerini kurar.

3. Doğru: B. Python program çalışırken nesneye yeni nitelik eklenmesine izin verir. Bu yüzden yanlış yazılmış ad hiçbir uyarı vermeden yeni bir nitelik olur; hata ancak sonuç yanlış çıkınca fark edilir.

  • A yanlış: Metotlarda self unutmak sık görülen bir hata olduğu için ilk akla gelen budur. Ama self parametresi tanımda vardır.
  • C yanlış: Başlangıç değerinin __init__ içinde sabitlendiği düşünülebilir. Oysa __init__ içinde kurulan nitelikler sonradan değiştirilebilir.
  • D yanlış: Yanlış yazılmış bir adın hata vermesi beklenir. Ama AttributeError olmayan bir niteliği okurken çıkar; olmayan bir niteliğe değer atamak ise yeni nitelik oluşturur.
  • E yanlış: Fonksiyon içinden dış bir adı değiştirmek için global gerektiği bilgisi buraya taşınabilir. Ama global modül düzeyindeki adlar içindir; nitelikler self üzerinden yazılır.

4. Doğru: A. first.deposit(50) çağrısında self → first; second.deposit(50) çağrısında self → second olur.

  • B yanlış: Dönüş değeri return ile verilir.
  • C yanlış: self en son oluşturulan nesne değil, metodu çağrılan nesnedir.
  • D yanlış: Sınıfın kendisini alan parametre clstir ve @classmethod ile kullanılır.
  • E yanlış: self her metotta aynı adla yazıldığı için anahtar sözcük sanılabilir. Ama self geleneksel bir parametre adıdır; yerine başka bir ad da yazılabilir.

5. Doğru: A. Python örneği ilk argüman olarak gönderir ama metodun hiç parametresi yoktur. Lamp().turn_on() çağrısı Lamp.turn_on(ornek) gibi çalışır; tanımda self olmadığı için argüman sayısı tutmaz.

  • B yanlış: Gövdede tanımsız bir self kullanıldığı için bu hata akla yatkındır. Ama hata, gövdeye hiç girilmeden, argümanlar parametrelerle eşleştirilirken çıkar.
  • C yanlış: Parametresiz bir metot tanımı yanlış göründüğü için hatanın tanımda çıkacağı düşünülebilir. Ama tanım geçerlidir; sorun çağrı anında ortaya çıkar.
  • D yanlış: self her metotta kullanıldığı için dilin ayırdığı bir ad gibi görünür. Ama self sihirli bir ad değildir; parametre olarak yazılmalıdır.
  • E yanlış: Metodun adı ve gövdesi niyeti açıkça gösterdiği için çalışacağı sanılabilir. Ama gövde hiç çalışmaz.

6. Doğru: E. value her örnekte __init__ içinde ayrı ayrı kurulur; sayaçlar bağımsızdır.

  • A yanlış: Bu, value sınıf niteliği olsaydı ve paylaşılsaydı beklenirdi.
  • B yanlış: first iki kez artırılmıştır.
  • C yanlış: second yalnızca bir kez artırılmıştır.
  • D yanlış: increment çağrıları durumu gerçekten değiştirir.

7. Doğru: B. ada.school = ... yalnızca ada örneğinde aynı adlı bir nitelik oluşturur ve sınıftaki değeri o nesne için gölgeler. Sınıf ve diğer örnek etkilenmez.

  • A yanlış: Atama hata vermez; yeni bir örnek niteliği oluşturur.
  • C yanlış: ece kendisinde böyle bir nitelik olmadığı için sınıftaki değeri görür.
  • D yanlış: ada.school okunurken ada nesnesine atanan yeni nitelik bulunur.
  • E yanlış: Bunun için Student.school = ... yazılması gerekirdi.

8. Doğru: B. Bir nesneye yapılan atama yalnız o nesneyi etkiler; sınıf niteliği sınıfın adıyla değiştirilir.

  • A yanlış: global modül düzeyindeki adlar içindir; sınıf niteliğine erişmez.
  • C yanlış: Geçici bir örnek oluşturur ve yalnız onu etkiler.
  • D yanlış: Yalnız ada örneğinde gölgeleyen bir nitelik oluşur.
  • E yanlış: self.school ataması da o nesnede bir örnek niteliği oluşturur.

9. Doğru: D. first.members.append(...) yeni bir nitelik oluşturmaz; sınıftaki listeyi yerinde değiştirir. first.members is second.members sonucu Truedur.

  • A yanlış: Tek bir append çağrısı vardır.
  • B yanlış: Bu ancak liste __init__ içinde oluşturulsaydı doğru olurdu.
  • C yanlış: members bir listedir; append onu None yapmaz.
  • E yanlış: Örnekte olmayan ad sınıfta aranır ve bulunur.

10. Doğru: C. Böylece her yeni takım için ayrı bir liste oluşturulur ve takımlar aynı listeyi paylaşmaz.

  • A yanlış: Her yeni takımın boş listeyle başlaması sorunu çözüyor gibi görünür. Ama self.members hâlâ sınıftaki ortak listedir; yeni bir takım oluşturmak öteki takımların üyelerini de siler.
  • B yanlış: __init__’ten sonra yazılan satırın her örnekte çalışacağı düşünülebilir. Ama satır hâlâ sınıf gövdesindedir, metodun içinde değildir; yine bir kez çalışır ve ortak bir sınıf niteliği oluşturur.
  • D yanlış: self. öneki niteliği örneğe bağladığı için bu yazım doğru görünür. Ama sınıf gövdesinde self diye bir ad yoktur; self yalnız metotların parametresidir. Satır NameError verir.
  • E yanlış: list() bir çağrı olduğu için her örnekte yeni liste vereceği sanılabilir. Ama list() de tek bir liste oluşturur; satır sınıf gövdesinde olduğu için yine yalnız bir kez çalışır.

11. Doğru: C. Bakiyeyi doğrudan değiştiren her kod parçası kuralı kendisi yeniden yazmak zorunda kalır. Metot ise kuralı nesnenin içinde, tek yerde tutar; kontrol orada yapılır.

  • A yanlış: Metot kullanma önerisi, dilin doğrudan atamayı yasakladığı izlenimini verir. Oysa nitelikler değiştirilebilir; dil engel olmadığı için bu kararı tasarımda vermek gerekir.
  • B yanlış: Önerilmeyen bir yazımın geçersiz olduğu düşünülebilir. Ama söz dizimi doğrudur; sorun tasarımdadır.
  • D yanlış: Metot çağrısı daha düzenli göründüğü için daha verimli sanılabilir. Ama hız farkı konu değildir; gerekçe, kuralın hangi nesnede durduğudur.
  • E yanlış: Nitelik adı artık çağrıda görünmediği için gizlenmiş gibi durur. Ama ad gizlenmez; değişiklik bu kez metodun içinden yapılır.

Tek sayfa özet

  • Sınıf bir tür tanımıdır. Nesne, o sınıfı çağırarak oluşturulan örnektir: BankAccount("Ada", 500).
  • __init__ yeni nesnenin niteliklerine ilk değerlerini verir. Nesneyi oluşturmaz, hazır gelen nesneyi başlatır.
  • self metodun çağrıldığı nesnedir. C’deki işaretçi parametresinin karşılığıdır, ama çağrıda yazılmaz: account.deposit(50), BankAccount.deposit(account, 50) gibi çalışır.
  • Aynı sınıftan oluşturulan her nesnenin kendi nitelikleri vardır.
  • Örnek niteliği o nesneye aittir, sınıf niteliğini bütün örnekler ortak görür.
  • Bir nesneye sınıf niteliğiyle aynı adlı bir nitelik atamak, o nesne için sınıftaki değeri gölgeler. Sınıf niteliğini sınıfın adıyla değiştirin.
  • Her nesnenin kendi mutable koleksiyonu gerekiyorsa koleksiyonu __init__ içinde oluşturun.
  • Python program çalışırken nesneye yeni nitelik eklenmesine izin verir. Yanlış yazılmış bir nitelik adı hata vermeden yeni bir nitelik oluşturur.
  • Bir metodu hangi sınıfa yazacağınıza karar verirken şunu sorun: bu iş hangi nesnenin sorumluluğu?

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

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

  • Sınıf ile örnek ayrımını açıklar ve class ile kendi türünü tanımlar.
  • __init__ içinde örnek niteliklerini oluşturur.
  • self parametresinin, metodu çağrılan nesne olduğunu gösterir.
  • Aynı sınıftan oluşturulan nesnelerin niteliklerinin birbirinden bağımsız olduğunu belirler.
  • Sınıf niteliği ile örnek niteliğini ayırt eder ve gölgelemeyi tanır.
  • Paylaşılan mutable sınıf niteliğinin riskini fark eder ve koleksiyonu __init__ içinde oluşturur.
  • Niteliği doğrudan atamak yerine bir metotla değiştirmenin gerekçesini açıklar.
Başa dön