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.

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ı

NoteSı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.

Banka hesabındaki beş işi C’de nasıl yapardınız?

Bir bankanın hesap programını yazıyorsunuz. Her hesabın bir sahibi ve bir bakiyesi var. Program şu beş işi yapacak:

  1. Yeni hesap açmak: sahibin adı ve başlangıç bakiyesi bir arada dursun
  2. Bir hesaba para yatırmak
  3. Ada’nın ve Ece’nin hesaplarını ayrı tutmak: Ada’nın hesabına yatırılan para Ece’nin bakiyesini değiştirmesin
  4. Bütün hesaplarda aynı olan bir bilgiyi, örneğin bankanın adını, her hesaba ayrı ayrı yazmadan tutmak
  5. Para çekme kuralını tek yerde tutmak: bakiye yetmiyorsa para çekilmesin

Programlama Temelleri’nde C ile yazdığınız programları düşünün. Bu işlerin her birini C’de nasıl yapardınız? Okumaya devam etmeden önce her iş için bir satır yazın. Geçen hafta kırtasiyenin stok kaydını bir sözlük ve onu değiştiren fonksiyonlarla tuttuk, bu bilgiyle de cevap verebilirsiniz: “struct ve onu alan bir fonksiyon yazarım” da “sözlük ve fonksiyon yazarım” da bir cevap.

Bölümde bu beş işin bir Python sınıfıyla nasıl yapıldığını göreceğiz. Bölümün sonunda listenize döneceğiz.

İ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.

Note__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 = []
WarningMutable 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.

WarningBü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.

Soruya dönelim: banka hesabının beş işi

Bölümün başında banka programındaki beş işi C’de nasıl yapacağınızı sormuştuk. Bir Python sınıfıyla karşılıkları şöyle:

İş C’de ya da 6. haftada Bu haftaki Python yolu Dikkat
Yeni 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
Hesapları 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
Ortak 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

Listenizi tabloyla karşılaştırın. İlk üç iş için C’de yazdığınız 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. 4. işte global değişken yazdıysanız, sınıf niteliği bu bilgiyi programın her yerine değil yalnız o sınıfa bağlar. 5. iş henüz yarım: kural metotta 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 maddeleri de bu mantıkla yazılır.

Madde 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

Madde 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

Madde 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

Madde 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

Madde 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

Madde 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

Madde 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

Madde 8. Sınıf düzeyindeki bir değeri gerçekten değiştirmek isteyen kod ne yazmalıdır?

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

Madde 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. []; her örneğin kendi listesi vardır
  • C. None
  • D. ['Ada']; iki takım aynı listeyi görür
  • E. AttributeError; second için members tanımlı değildir

Madde 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. Her metodun başında self.members = self.members.copy() çağrılmalıdır
  • B. Her örnek için ayrı bir sınıf tanımlanmalıdır
  • C. Liste __init__ içinde self.members = [] ile oluşturulmalıdır
  • D. members demet yapılmalıdır
  • E. Sınıf gövdesindeki members = [] satırı members = list() yapılmalıdır

Madde 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

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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.

Madde 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ış: Karmaşık ve kırılgandır; sorun listenin nerede oluşturulduğudur.
  • B yanlış: Sınıf, aynı tanımdan çok sayıda nesne oluşturmak için yazılır.
  • D yanlış: Demete üye eklenemez; üstelik istenen, her takımın kendi listesinin olmasıdır.
  • E yanlış: 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.

Madde 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.
Back to top