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

Artık kendi nesne türlerimizi tanımlayabiliriz. Bir sınıf (class), belirli türde nesnelerin hangi duruma ve davranışlara sahip olacağını tanımlar. Sınıftan oluşturulan her örnek (instance) kendi durumunu taşır.

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 kurma
  • self parametresinin çağrının hedefindeki nesneyi temsil etmesi
  • aynı sınıftan üretilen nesnelerin bağımsız durum taşıması
  • sınıf niteliği ile örnek niteliği; gölgeleme
  • paylaşılan mutable sınıf niteliğinin riski
  • durum değişikliğini metot üzerinden yapmanın gerekçesi
  • @classmethod ve @staticmethod
  • __new__ ve nesne oluşturma ayrıntısı
  • __slots__

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

Sözlükle taşıdığımız kaydı sınıfa çevirmek ne kazandırır?

Geçen bölümde bir kaydın hangi anahtarları taşıması gerektiği ve hangi fonksiyonların onu değiştirebildiği dağılmıştı. Sınıf bu sınırı çizer: durum ve onu değiştiren davranış tek bir ad altında toplanır.

İlk sınıfımız

BankAccount sınıftır; account ise bu sınıftan oluşturulmuş bir nesneye referanstır. __init__, yeni örneğin başlangıç durumunu kurar.

Note__init__ “nesneyi yoktan yaratan fonksiyon” değildir

Python veri modelinde nesnenin oluşturulması ile başlatılması ayrıdır. Bu derste __new__ ayrıntısına girmiyoruz; pratik olarak __init__’i başlangıç durumunu kurduğumuz metot olarak kullanacağız.

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

init ve self akışı

self nedir?

self, metot çağrısının yapıldığı o anki nesneye erişmemizi sağlar. first üzerinden çağrılan metotta selffirst; second üzerinden çağrıda selfsecond nesnesidir.

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ı

Davranışı sınıfa eklemek

account.deposit(50) çağrısını zihinsel olarak BankAccount.deposit(account, 50) biçiminde düşünebilirsiniz. Normal kullanım ilk biçimdir; ikincisi self’in nereden geldiğini anlamaya yardım eder.

Metot yalnızca “sınıf içindeki fonksiyon” değildir

Tasarım açısından daha önemli fikir şudur:

Metot, nesnenin sorumluluğuna ait davranışı ifade eder.

deposit() davranışının BankAccount içinde olması, bakiye değişikliğinin hesap nesnesinin sorumluluğunda olduğunu söyler.

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

Her nesne kendi value durumunu taşır.

Sınıf niteliği ile örnek niteliği aynı şey değildir

name örnek niteliğidir. school sınıfta tanımlanmıştır ve örnekler, kendilerinde aynı adlı bir nitelik yoksa sınıftaki değeri görür.

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)

Şu atama sınıf niteliğini değiştirmez:

ada.school = ... yalnızca ada nesnesinde aynı adlı bir örnek niteliği oluşturur; bu nitelik sınıftaki school değerini o nesne için gölgeler. Student.school ve ece.school değişmez. Sınıf düzeyindeki değeri gerçekten değiştirmek istiyorsanız niyeti açıkça Student.school = ... ile ifade edin.

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

members sınıf niteliği olduğu için iki nesne de aynı listeyi görür. Her takımın bağımsız üye listesine sahip olmasını istiyorsak:

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

Bütün örneklerce gerçekten paylaşılacak veri için sınıf niteliği anlamlı olabilir. Her nesnenin kendi listesi, sözlüğü veya kümesi gerekiyorsa mutable nesneyi __init__ içinde oluşturun.

Yazım hatası bazen sessizce yeni nitelik oluşturur

C gibi statik yapısı daha katı dillerden geçerken şaşırtıcı bir nokta şudur: Python’da çoğu normal sınıf örneğine çalışma sırasında yeni nitelik eklenebilir.

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

    def reset(self):
        self.blance = 0  # balance yerine yanlışlıkla blance

self.blance = 0 satırı çoğu durumda hemen hata vermez; yanlış adla yeni nitelik oluşturur. Daha sonra balance hâlâ 100 olduğu için hata mantıksal olarak ortaya çıkar.

WarningNitelik adlarını tutarlı kullanın

Editörün tamamlama/uyarı özelliklerinden yararlanın ve sınıfın durumunu tek tek farklı yerlerde doğaçlama oluşturmamaya çalışın. __init__ içinde beklenen örnek niteliklerini açıkça kurmak bu hataları azaltır.

Kavram radarı: @classmethod ve @staticmethod

Bu hafta yazmanız gereken temel metot türü örnek metodudur ve ilk parametresi self olur. İnternette iki dekoratöre sık rastlayacaksınız:

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

    @staticmethod
    def is_valid_name(name):
        ...
  • @classmethod ilk parametre olarak örnek yerine sınıfı (cls) alır; alternatif oluşturucu gibi sınıf düzeyindeki davranışlarda kullanılabilir.
  • @staticmethod otomatik self veya cls almaz; sınıfın ad alanında duran, ancak örnek durumuna ihtiyaç duymayan yardımcı davranış için kullanılabilir.

Bu iki mekanizma bu haftanın üretim beklentisi değildir. Öncelik, örnek durumu kullanan davranışta normal instance method yazmayı doğru öğrenmektir.

Sorumluluğu doğru yere koymak

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

İkinci yaklaşım “para çekmenin kuralları hesabın sorumluluğundadır” dememizi sağlar. Gelecek hafta withdraw içinde geçerli durum kurallarını koruyacağız.

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? 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. İki öğrenci üretin; yalnızca ilk öğrenciye student.school = "X" atayın. Üç değeri (first.school, second.school, Student.school) yazdırıp sonucu açıklayın.

Küçük üretim görevi — Book sınıfı

Bir Book sınıfı oluşturun:

  • 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 diğerini değiştirmediğini gösterin.

Şimdilik aynı kitabı iki kez ödünç alma gibi geçersiz durumları engellemiyoruz. Gelecek hafta tam olarak bunu yapacağız.

Çalışır kod teslimi: En az iki Book nesnesi üretin; borrow() ve return_book() çağrıları sonrası durumları assert ile kontrol edin. Ayrıca sınıf/örnek niteliği gölgelemesini gösteren en az bir kısa deney ekleyin.

Sınav provası

Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Ç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. Sınıf, belirli türde nesnelerin hangi duruma ve davranışa sahip olacağını tanımlar; nesne o tanımdan üretilmiş, kendi durumunu taşıyan çalışma anı örneğidir
  • D. Her sınıftan yalnızca bir nesne üretilebilir
  • E. Sınıf çalışma anında, nesne yazım anında var olur

Madde 2. __init__ metodu ne yapar?

  • A. Nesne silinirken çalışır ve kaynakları serbest bırakır
  • B. Örneğin metinsel temsilini üretir
  • C. Nesneyi yoktan yaratır ve belleği ayırır
  • D. Yeni üretilen örneğin başlangıç durumunu kurar; self üzerinden örnek niteliklerini oluşturur
  • 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â 100dü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. balance __init__ içinde tanımlandığı için salt okunurdur
  • C. AttributeError yükselir ve atama yapılmaz
  • D. Metot içinde nitelik değiştirmek için global bildirimi gerekir
  • E. self.blance yazım hatası yeni bir nitelik oluşturur; balance hiç değişmez ve hata ancak sonuç yanlış çıkınca fark edilir

Madde 4. Bir metot içindeki self neyi temsil eder?

  • A. Metodun üzerinde çağrıldığı o anki nesneyi
  • B. Metodun dönüş değerini
  • C. Son üretilen örneği
  • D. Sınıfın kendisini
  • E. Python’ın ayrılmış bir anahtar sözcüğünü

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. NameError yükselir; self tanımsızdır
  • B. TypeError yükselir; çağrı örneği ilk argüman olarak geçirir ama metot hiç parametre tanımlamamıştır
  • C. Metot tanımı sözdizimi 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 1
  • D. 2 2
  • E. 0 0

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 Başka MYO Samsun MYO
  • C. Üç değer de Samsun MYO
  • D. Başka MYO Samsun MYO 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. ada.school = "Yeni MYO"
  • D. self.school = "Yeni MYO" (herhangi bir metot içinde)
  • E. Student.school = "Yeni MYO"; niyet sınıf üzerinden açıkça ifade edilmelidir

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']; members sınıf niteliğidir ve bütün örnekler aynı listeyi görür
  • B. ['Ada', 'Ada']
  • C. []; her örneğin kendi listesi vardır
  • D. None
  • 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. Liste __init__ içinde self.members = [] ile oluşturulmalıdır
  • C. Her örnek için ayrı bir sınıf tanımlanmalı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özdizimi hatası üretir
  • C. Para çekmenin kuralları hesabın sorumluluğunda toplanır; doğrulama tek bir yerde yapılabilir
  • D. Metot çağrısı doğrudan atamadan daha hızlı çalışır
  • E. Metot kullanmak nitelik adını gizler

Madde 1 — Doğru: C. Sınıf bir tür tanımıdır; her örnek kendi nitelik değerlerini taşır.

  • A yanlış: Sınıf hem durumun biçimini hem davranışı tanımlar.
  • B yanlış: Örnek bir kopya değildir; metotlarını sınıftan okur.
  • D yanlış: İstenildiği kadar örnek üretilebilir.
  • E yanlış: İkisi de çalışma anında vardır; sınıf tanımı da yürütülen bir ifadedir.

Madde 2 — Doğru: D. Python’da nesnenin oluşturulması ile başlatılması ayrıdır; __init__ başlatma adımıdır.

  • A yanlış: Bu bir yıkıcı davranışıdır; __init__ üretimde çalışır.
  • B yanlış: Bu __str__ ve __repr__ metotlarının işidir.
  • C yanlış: Oluşturma ayrı bir adımdır; __init__ kendisine verilen örneği başlatır.
  • E yanlış: Sınıf nitelikleri sınıf gövdesinde tanımlanır.

Madde 3 — Doğru: E. Python örneğe çalışma sırasında yeni nitelik eklenmesine izin verir. Bu esneklik yazım hatasını sessizleştirir.

  • A yanlış: self parametresi tanımda vardır.
  • B yanlış: __init__ içinde kurulan nitelikler sonradan değiştirilebilir.
  • C yanlış: Okuma sırasında hata çıkar; yazma sırasında yeni nitelik oluşur.
  • D yanlış: 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 selffirst; second.deposit(50) çağrısında selfsecond olur.

  • B yanlış: Dönüş değeri return ile verilir.
  • C yanlış: Üretim sırası değil, çağrının hedefi belirleyicidir.
  • D yanlış: Sınıfı temsil eden parametre clstir ve @classmethod ile kullanılır.
  • E yanlış: self anahtar sözcük değil, geleneksel bir parametre adıdır.

Madde 5 — Doğru: B. Lamp().turn_on() zihinsel olarak Lamp.turn_on(ornek) demektir. Tanımda self yoksa argüman sayısı uyuşmaz.

  • A yanlış: Hata gövdeye hiç girilmeden, çağrı kurulurken oluşur.
  • C yanlış: Tanım geçerlidir; sorun çağrı anında ortaya çıkar.
  • D yanlış: self sihirli bir ad değildir; parametre olarak yazılmalıdır.
  • E yanlış: Gövde hiç çalışmaz.

Madde 6 — Doğru: C. 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.
  • D yanlış: second yalnızca bir kez artırılmıştır.
  • E yanlış: increment çağrıları durumu gerçekten değiştirir.

Madde 7 — Doğru: D. 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.
  • B yanlış: ece kendisinde böyle bir nitelik olmadığı için sınıftaki değeri görür.
  • C yanlış: ada üzerinde oluşan örnek niteliği okunur.
  • E yanlış: Bunun için Student.school = ... yazılması gerekirdi.

Madde 8 — Doğru: E. Örnek üzerinden atama yalnız o nesneyi etkiler; sınıf niteliği sınıf adıyla güncellenir.

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

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

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

Madde 10 — Doğru: B. Böylece her örnek üretiminde yeni bir liste nesnesi kurulur ve paylaşım ortadan kalkar.

  • A yanlış: Karmaşık ve kırılgandır; sorun listenin nerede oluşturulduğudur.
  • C yanlış: Sınıfın amacı aynı tanımdan çok örnek üretmektir.
  • D yanlış: Demet üye eklemeye izin vermez; ihtiyaç bağımsızlıktır.
  • E yanlış: Aynı sonucu verir; nesne yine sınıf düzeyinde bir kez oluşur.

Madde 11 — Doğru: C. Durumu doğrudan değiştiren her çağrı kuralı kendi başına tekrar etmek zorunda kalır. Metot, kuralı nesnenin içinde tutar.

  • A yanlış: Değiştirilebilir; bu yüzden tasarım kararı gerekir.
  • B yanlış: Geçerli bir ifadedir; sorun anlamsaldır.
  • D yanlış: Gerekçe başarım değil, sorumluluk dağılımıdır.
  • E yanlış: Ad gizlenmez; değişen, değişikliğin nereden geçtiğidir.

Tek sayfa özet

  • Sınıf bir tür tanımı; nesne o sınıfın çalışma anındaki örneğidir.
  • self, çağrının hedefindeki nesneye erişim sağlar.
  • __init__ başlangıç durumunu kurar.
  • Örnek nitelikleri nesneye özgü durum taşır; sınıf nitelikleri sınıf düzeyinde paylaşılabilir.
  • Örnek üzerinde aynı adlı nitelik oluşturmak sınıf niteliğini gölgeleyebilir.
  • Her nesnenin kendi mutable koleksiyonu gerekiyorsa koleksiyonu __init__ içinde oluşturun.
  • Python’ın dinamik nitelikleri esnektir; yazım hatalarını da sessizleştirebilir.
  • İyi OOP tasarımında temel soru “bu davranış kimin sorumluluğu?”dur.

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 kurar.
  • self parametresinin çağrının hedefindeki nesneyi temsil ettiğini gösterir.
  • Aynı sınıftan üretilen nesnelerin bağımsız durum taşıdığını belirler.
  • Sınıf niteliği ile örnek niteliğini ayırır ve gölgelemeyi tanır.
  • Paylaşılan mutable sınıf niteliğinin riskini fark eder ve koleksiyonu __init__ içinde kurar.
  • Durum değişikliğini doğrudan atama yerine metot üzerinden yapmanın gerekçesini açıklar.
Back to top