Python'da sınıf class Ad: satırıyla tanımlanır; sınıfı fonksiyon gibi çağırdığınızda (Ad()) yeni bir örnek (instance) yani nesne elde edersiniz. Nesne oluşturulurken Python, sınıfta tanımlıysa __init__ metodunu otomatik çalıştırır ve çağrıda verdiğiniz argümanları ona iletir. Metotların ilk parametresi olan self, metodu çağıran nesnenin kendisidir; a.yatir(50) yazdığınızda Python bunu Hesap.yatir(a, 50) olarak çalıştırır.
Bu yazıda sınıf tanımının arka planda nasıl işlediğini, __init__ ve self ilişkisini, sınıf değişkeni ile örnek değişkeni farkını, öznitelik arama sırasını, @classmethod ve @staticmethod kullanımını, alt çizgili adlardaki gizlilik kurallarını ve en sık yapılan hataları Python 3.15.2 ile çalıştırılmış örneklerle anlatıyoruz. Python'a yeni başlıyorsanız konu sırası için Python öğrenme rehberine, fonksiyon temelleri için de fonksiyon tanımlama yazısına bakabilirsiniz.
Sınıf tanımı çalıştırıldığında ne olur?

class ifadesi, def gibi çalıştırılan bir deyimdir: Python o satıra gelene kadar sınıf yoktur. Bu yüzden bir sınıfı if bloğunun ya da bir fonksiyonun içinde tanımlamak da mümkündür. Sınıf gövdesine girildiğinde yeni bir ad alanı (namespace) açılır ve gövdedeki her atama, her def bu ad alanına yazılır. Gövde bittiğinde bu ad alanını saran bir sınıf nesnesi oluşturulur ve sınıfın adına bağlanır. Yani sınıfın kendisi de bir nesnedir; değişkene atanabilir, fonksiyona argüman olarak verilebilir, çalışma sırasında yeni öznitelik alabilir.
Sınıf nesnesi iki işlem destekler: öznitelik erişimi (Hesap.banka) ve örnek oluşturma (Hesap(...)). Gövdenin ilk satırına yazdığınız metin, sınıfın belge dizesi (docstring) olur ve __doc__ özniteliğinden okunur. Aşağıdaki örnekte banka sınıf düzeyinde, sahip ve bakiye ise her nesneye özel tanımlanıyor:
class Hesap:
"""Basit bir banka hesabı."""
banka = 'Örnek Bank' # sınıf değişkeni
def __init__(self, sahip, bakiye=0):
self.sahip = sahip # örnek değişkeni
self.bakiye = bakiye
def yatir(self, tutar):
self.bakiye += tutar
return self.bakiye
a = Hesap('Ayşe', 100)
b = Hesap('Mehmet')
print(a.yatir(50))
print(b.bakiye)
print(a.banka, b.banka)
print(type(a).__name__, isinstance(a, Hesap))
print(Hesap.__doc__)
# çıktı: 150
# çıktı: 0
# çıktı: Örnek Bank Örnek Bank
# çıktı: Hesap True
# çıktı: Basit bir banka hesabı.type(a) nesnenin sınıfını döndürür; aynı bilgi a.__class__ özniteliğinde de durur. isinstance() ise nesnenin verilen sınıftan ya da onun alt sınıflarından birinden olup olmadığını sorar. Python'da her değer bir nesnedir ve her nesnenin bir sınıfı (tipi) vardır; int, str ya da list de birer sınıftır.
__init__ ve self: nesne nasıl kurulur?
Sınıfı çağırmak önce boş bir nesne üretir. Bu üretim işini __new__ özel metodu yapar; çoğu sınıfta onu yazmanız gerekmez, object sınıfından gelen sürüm yeterlidir. Nesne oluştuktan sonra, çağıran koda döndürülmeden hemen önce __init__ çalışır ve nesneyi başlangıç durumuna getirir. Bu nedenle __init__ için "yapıcı" demek yaygın olsa da teknik olarak "başlatıcı" demek daha doğrudur. Belgelere göre __init__ None dışında bir değer döndüremez; döndürürse çalışma anında TypeError oluşur.
self ise Python için özel bir anahtar kelime değildir, yalnızca güçlü bir adlandırma uzlaşısıdır. İlk parametreye başka ad verseniz de kod çalışır, ancak okuyan herkesin kafası karışır. Önemli olan şudur: Python'da metot gövdesinden nesnenin özniteliklerine kısa yoldan erişim yoktur. Bir öznitelik okumak ya da başka bir metodu çağırmak için her zaman self.ad ya da self.metot() yazarsınız. Bu kural yerel değişkenlerle örnek değişkenlerinin karışmasını engeller.
Metot nesnesi: x.f() neden argümansız çalışır?
Sınıfta tanımlanan bir fonksiyona örnek üzerinden eriştiğinizde (s.soyle) düz bir fonksiyon değil, bağlı metot (bound method) nesnesi alırsınız. Bu nesne iki şeyi birlikte taşır: örneğin kendisini ve sınıftaki fonksiyonu. Bağlı metodu çağırdığınızda Python argüman listesinin başına örneği ekler ve fonksiyonu öyle çalıştırır. Bağlı metodu bir değişkende saklayıp sonra da çağırabilirsiniz:
class Selam:
def soyle(self, ad):
return f'Merhaba {ad}'
s = Selam()
m = s.soyle # çağırmadan sakla
print(m('Zeynep'))
print(Selam.soyle(s, 'Zeynep')) # aynı çağrı
print(m.__self__ is s)
print(m.__func__ is Selam.soyle)
print(type(m).__name__, type(Selam.soyle).__name__)
# çıktı: Merhaba Zeynep
# çıktı: Merhaba Zeynep
# çıktı: True
# çıktı: True
# çıktı: method functionm.__self__ metodun bağlı olduğu örneği, m.__func__ ise asıl fonksiyon nesnesini verir. Bu mekanizmanın arkasında betimleyici (descriptor) protokolü vardır; fonksiyonlar sınıf özniteliği olarak okunduklarında kendilerini bağlı metoda dönüştürür. Ayrıntısını property ve descriptor yazısında anlatıyoruz.
Bu mekanizmanın ilginç bir sonucu var: sınıf özniteliği olan her fonksiyon nesnesi, örnekler için bir metot tanımlar. Fonksiyonun sınıf gövdesinin içinde yazılmış olması gerekmez; dışarıda tanımlanmış bir fonksiyonu sınıf gövdesinde bir ada atamak da aynı etkiyi yapar. Resmî eğitim bunun mümkün olduğunu ama genellikle okuyanın kafasını karıştırdığını not eder. Aynı nesnenin başka bir metodunu çağırmak için de yine self kullanılır: self.ekle(x). Metotlar global adlara sıradan fonksiyonlar gibi erişir; bir metodun global kapsamı, tanımlandığı modüldür. Sınıfın kendi gövdesi ise hiçbir zaman global kapsam olarak kullanılmaz, yani metot içinden sınıf değişkenine self.banka ya da Hesap.banka diye erişmeniz gerekir; yalın banka adı bulunamaz. Kapsam kurallarının tamamı için kapsam ve global/nonlocal yazısına bakabilirsiniz.
Sınıf değişkeni ve örnek değişkeni
Sınıf gövdesinde doğrudan atanan adlar sınıf değişkenidir ve tüm örnekler tarafından paylaşılır. __init__ içinde self.ad = ... şeklinde atananlar ise örnek değişkenidir ve her nesneye özeldir. Sayı ve metin gibi değiştirilemez (immutable) değerlerde paylaşım sorun çıkarmaz. Ama liste ya da sözlük gibi değiştirilebilir bir nesneyi sınıf değişkeni yaparsanız bütün örnekler aynı nesneyi değiştirir. Resmî eğitimdeki klasik örneğin Türkçe uyarlaması:
class Kopek:
numaralar = [] # HATALI: tüm örnekler aynı listeyi paylaşır
def __init__(self, ad):
self.ad = ad
def ogret(self, numara):
self.numaralar.append(numara)
k1 = Kopek('Karabaş')
k2 = Kopek('Pamuk')
k1.ogret('otur')
k2.ogret('yuvarlan')
print(k1.numaralar)
print(k1.numaralar is k2.numaralar)
class DogruKopek:
def __init__(self, ad):
self.ad = ad
self.numaralar = [] # her köpeğe ayrı liste
def ogret(self, numara):
self.numaralar.append(numara)
d1 = DogruKopek('Karabaş')
d2 = DogruKopek('Pamuk')
d1.ogret('otur')
d2.ogret('yuvarlan')
print(d1.numaralar, d2.numaralar)
# çıktı: ['otur', 'yuvarlan']
# çıktı: True
# çıktı: ['otur'] ['yuvarlan']Öznitelik arama sırası: önce örnek, sonra sınıf
nesne.ad okunduğunda Python önce nesnenin kendi sözlüğüne (nesne.__dict__) bakar; bulamazsa sınıfa, oradan da taban sınıflara geçer. Bu yüzden aynı ad hem örnekte hem sınıfta varsa örnekteki kazanır. Bir örneğe atama yapmak sınıfı değiştirmez; yalnızca o örneğin sözlüğüne yeni bir anahtar ekler. vars(nesne) bu sözlüğü gösterir:
class Depo:
amac = 'saklama'
bolge = 'batı'
d1 = Depo()
d2 = Depo()
d2.bolge = 'doğu' # yalnızca d2'nin sözlüğüne yazar
print(d1.bolge, d2.bolge)
print(vars(d1), vars(d2))
Depo.bolge = 'kuzey' # sınıfı değiştir
print(d1.bolge, d2.bolge)
del d2.bolge # örnek özniteliğini sil
print(d2.bolge)
# çıktı: batı doğu
# çıktı: {} {'bolge': 'doğu'}
# çıktı: kuzey doğu
# çıktı: kuzeySınıf değişkenini sınıf üzerinden değiştirdiğinizde (Depo.bolge = ...) kendi kopyası olmayan bütün örnekler yeni değeri görür. Kendi kopyası olan örnek etkilenmez; o kopya del ile silinince arama yeniden sınıfa düşer. Bu davranış, sınıf değişkenlerini "ortak varsayılan" olarak kullanmanın hem gücü hem de tuzağıdır.
classmethod ve staticmethod

Normal bir metot ilk argüman olarak örneği alır. @classmethod ile işaretlenen metot ise ilk argüman olarak sınıfı (geleneksel adıyla cls) alır; sınıf üzerinden de örnek üzerinden de çağrılabilir. Alt sınıf üzerinden çağrıldığında cls alt sınıfın kendisidir. Bu özellik, "metinden oluştur", "dosyadan oluştur" gibi alternatif kurucular yazmak için biçilmiş kaftandır. @staticmethod ise hiçbir örtük argüman almaz; sınıfın ad alanında duran sıradan bir fonksiyondur.
class Tarih:
def __init__(self, gun, ay, yil):
self.gun, self.ay, self.yil = gun, ay, yil
@classmethod
def metinden(cls, metin):
gun, ay, yil = map(int, metin.split('.'))
return cls(gun, ay, yil)
@staticmethod
def artik_yil_mi(yil):
return yil % 4 == 0 and (yil % 100 != 0 or yil % 400 == 0)
def __repr__(self):
return f'{type(self).__name__}({self.gun}, {self.ay}, {self.yil})'
class ResmiTatil(Tarih):
pass
print(Tarih.metinden('29.10.2026'))
print(ResmiTatil.metinden('23.04.2026')) # cls = ResmiTatil
print(Tarih.artik_yil_mi(2028), Tarih(1, 1, 2026).artik_yil_mi(2026))
# çıktı: Tarih(29, 10, 2026)
# çıktı: ResmiTatil(23, 4, 2026)
# çıktı: True Falsemetinden içinde Tarih(...) yerine cls(...) yazıldığı için ResmiTatil.metinden() doğru tipte nesne döndürdü. Belgeler Python'daki sınıf metotlarının C++ ya da Java'daki statik metotlardan farklı olduğunu, o anlamdaki karşılığın staticmethod olduğunu özellikle belirtir. Üç metot türünün farkı:
| Tür | İlk argüman | Tipik kullanım |
|---|---|---|
| Örnek metodu (def m(self)) | Örnek (self) | Nesnenin durumunu okumak veya değiştirmek |
| @classmethod (def m(cls)) | Sınıf (cls) | Alternatif kurucu, alt sınıfa uyumlu fabrika metodu |
| @staticmethod (def m()) | Yok | Sınıfla mantıksal ilgisi olan yardımcı fonksiyon |
Python'da private var mı? Tek ve çift alt çizgi
Python'da yalnızca nesnenin içinden erişilebilen gerçek anlamda gizli değişken yoktur. Bunun yerine bir uzlaşı vardır: adı tek alt çizgiyle başlayan öznitelikler (_not) genel API'nin parçası sayılmaz, uygulama ayrıntısıdır ve haber verilmeden değişebilir. Python bu kuralı zorlamaz, yalnızca okuyana sinyal verir.
En az iki alt çizgiyle başlayıp en fazla bir alt çizgiyle biten adlarda (__sifre) ise ad karıştırma (name mangling) devreye girer: Python bu adı sınıf tanımı içinde metin olarak _SinifAdi__sifre biçimine çevirir. Amaç gizlemek değil, alt sınıfların aynı adı kullandığında taban sınıfın iç değişkenini yanlışlıkla ezmesini önlemektir:
class Kasa:
def __init__(self):
self._not = 'iç kullanım' # tek alt çizgi: yalnızca bir uzlaşı
self.__sifre = '1234' # iki alt çizgi: ad karıştırma
k = Kasa()
print(k._not)
try:
print(k.__sifre)
except AttributeError as e:
print(e)
print(vars(k))
print(k._Kasa__sifre)
# çıktı: iç kullanım
# çıktı: 'Kasa' object has no attribute '__sifre'
# çıktı: {'_not': 'iç kullanım', '_Kasa__sifre': '1234'}
# çıktı: 1234Çıktıda görüldüğü gibi değer hâlâ _Kasa__sifre adıyla okunabiliyor; belgeler de kuralın kazaları önlemek için tasarlandığını vurgular. Bir ayrıntı: exec(), eval(), getattr(), setattr() ve doğrudan __dict__ erişimi bu dönüşümü uygulamaz; getattr(k, '__sifre') çağrısı karıştırılmış adı bulamaz. __init__ gibi iki yanında çift alt çizgi olan özel metot adları ise karıştırılmaz.
Python 3.13 ile gelen sınıf öznitelikleri
Python 3.13 sürüm notlarına göre derleyici artık sınıflara iki yeni öznitelik ekliyor. __static_attributes__, sınıf gövdesindeki herhangi bir fonksiyonda self.<ad> ile atanan öznitelik adlarını tutan bir demettir (tuple). __firstlineno__ ise sınıf tanımının ilk satır numarasını kaydeder. İlki, bir sınıfın hangi örnek özniteliklerini kurduğunu çalıştırmadan görmek için kullanışlıdır:
class Nokta:
def __init__(self, x, y):
self.x = x
self.y = y
def tasi(self, dx):
self.x += dx
self.son_hareket = dx
print(Nokta.__static_attributes__)
p = Nokta(1, 2)
p.etiket = 'A' # sınıf dışında eklenen öznitelik
print(vars(p))
print(p.__class__ is Nokta, Nokta.__name__, Nokta.__qualname__)
# çıktı: ('son_hareket', 'x', 'y')
# çıktı: {'x': 1, 'y': 2, 'etiket': 'A'}
# çıktı: True Nokta NoktaSınıf dışında sonradan eklenen etiket bu listede yer almaz, çünkü liste derleme sırasında sınıf gövdesindeki kod incelenerek oluşturulur. Python 3.15'in diğer yenilikleri için Python 3.15 yenilikler yazısına bakabilirsiniz.
Sık yapılan hatalar

Sınıflarla çalışırken karşılaşılan hataların büyük kısmı birkaç kalıba indirgenebilir. Aşağıdaki örnek bunlardan üçünü Python 3.15.2'nin gerçek mesajlarıyla gösteriyor:
class Sayac:
def artir(): # self unutuldu
pass
try:
Sayac().artir()
except TypeError as e:
print(e)
class Kotu:
def __init__(self):
return 42 # __init__ değer döndüremez
try:
Kotu()
except TypeError as e:
print(e)
try:
Sayac.artir2
except AttributeError as e:
print(e)
# çıktı: Sayac.artir() takes 0 positional arguments but 1 was given
# çıktı: __init__() should return None, not 'int'
# çıktı: type object 'Sayac' has no attribute 'artir2'- self'i unutmak: Metodu örnek üzerinden çağırınca Python örneği ilk argüman olarak gönderir. Parametre listesinde yer yoksa "takes 0 positional arguments but 1 was given" hatası alırsınız. Mesajdaki sayıların bir fazla görünmesi bu yüzdendir.
- __init__'ten değer döndürmek:
__init__yalnızca nesneyi hazırlar. Farklı bir nesne üretmek istiyorsanız@classmethodile bir fabrika metodu yazın. - Örnek değişkenini sınıf düzeyinde tanımlamak: Paylaşılmaması gereken listeleri sınıf gövdesine yazmak, yukarıdaki köpek örneğindeki gibi beklenmedik paylaşıma yol açar.
- Yazım hatalı öznitelik: Olmayan bir özniteliği okumak
AttributeErrorverir. Python 3.15 bu hatalarda daha yardımcı öneriler sunuyor; ayrıntılar sık hata mesajları yazısında. - Sınıfı çağırmadan kullanmak:
h = Hesapyazmak nesne değil sınıfın kendisini atar; parantezleri unutmayın.
Ne zaman class, ne zaman başka bir yapı?
Davranışla verinin birlikte yaşadığı, durumu zamanla değişen yapılar için sınıf doğal seçimdir: bir bağlantı havuzu, bir oyun karakteri, bir sipariş akışı gibi. Yalnızca birkaç adlandırılmış alanı bir arada taşımak istiyorsanız, Python eğitiminin de önerdiği gibi dataclasses modülü __init__, __repr__ ve __eq__ metotlarını sizin yerinize üretir. Değişmeyen küçük kayıtlar için namedtuple (collections yazısı), sabit seçenek kümeleri için Enum daha uygun olabilir. Sınıfınızın print(), ==, len() ya da + ile doğal davranmasını istiyorsanız sıradaki adım özel (dunder) metotlardır; sınıflar arasında kod paylaşımı için de kalıtım ve super() yazısına geçebilirsiniz.
Sık Sorulan Sorular
self bir anahtar kelime mi?
Hayır. self yalnızca ilk parametreye verilen geleneksel addır. Python ilk parametreye her zaman örneği yerleştirir; adı ne olursa olsun. Yine de okunabilirlik ve araç desteği için self dışında bir ad kullanmayın.
__init__ ile __new__ arasındaki fark nedir?
__new__ nesneyi oluşturur ve döndürür, __init__ oluşmuş nesneyi başlatır. __new__ genellikle int, str, tuple gibi değiştirilemez tiplerden türeyen sınıflarda ya da metasınıflarda yazılır. __new__ sınıfın örneğini döndürmezse __init__ hiç çağrılmaz.
Bir nesneye sınıfta olmayan bir öznitelik ekleyebilir miyim?
Evet, varsayılan olarak her nesnenin bir __dict__ sözlüğü vardır ve nesne.yeni = 1 çalışır. Bunu engellemek isterseniz __slots__ kullanabilirsiniz; property ve __slots__ yazısında örneği var.
Sınıf metodunu örnek üzerinden çağırmak hata mı?
Hata değildir. Tarih(1, 1, 2026).metinden(...) da çalışır; örnek yok sayılır ve yalnızca sınıfı kullanılır. Okunabilirlik açısından sınıf metotlarını sınıf adı üzerinden çağırmak daha açıktır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Python öğrenme rehberi · Kalıtım, super() ve MRO · Özel metotlar · property ve descriptor · dataclasses · Python nedir?