Python’da Sınıflar ve Nesneler: Nesne Yönelimli Programlamanın Temelleri
Python, nesne yönelimli programlama (NYP) paradigmasını güçlü bir şekilde destekleyen bir dildir. NYP, gerçek dünyadaki varlıkları ve kavramları modellememizi sağlayan, kodun yeniden kullanılabilirliğini, okunabilirliğini ve bakımını artıran bir yaklaşımdır. Bu rehberde, Python 3’te sınıfların nasıl oluşturulacağını ve nesnelerin nasıl tanımlanacağını adım adım öğrenecek, temel kavramları ve pratik uygulamaları keşfedeceksiniz.
Bir sınıf, belirli bir türdeki nesnelerin özelliklerini (niteliklerini) ve davranışlarını (metotlarını) tanımlayan bir “plan” veya “şablon”dur. Nesne ise bu sınıftan oluşturulmuş, somut bir örnektir. Örneğin, “Araba” bir sınıf olabilirken, “Benim Kırmızı Arabam” veya “Komşunun Mavi Arabası” bu Araba sınıfının birer nesnesidir.
Sınıf Tanımlama ve Temel Yapı Taşları
Bir Python sınıfı tanımlamak oldukça basittir. class anahtar kelimesini kullanırız, ardından sınıfın adını (genellikle PascalCase – her kelimenin ilk harfi büyük) ve iki nokta üst üste koyarız.
Bir Sınıfı Tanımlamak
Basit bir sınıf, sadece pass anahtar kelimesiyle bile tanımlanabilir. pass, Python’da hiçbir şey yapmayan bir yer tutucudur.
class Araba:
pass # Şimdilik bir şey yapmıyor
Bu sınıf henüz herhangi bir nitelik veya metoda sahip değil. Şimdi bu sınıfa özellikler ve davranışlar ekleyelim.
Yapıcı Metot (__init__)
Bir sınıfın en önemli metotlarından biri __init__ metodudur. Bu, bir sınıftan yeni bir nesne (örnek) oluşturulduğunda otomatik olarak çağrılan özel bir metottur. Genellikle nesnenin başlangıç durumunu ayarlamak, yani niteliklerini tanımlamak için kullanılır.
__init__ metodunun ilk parametresi her zaman self olmalıdır. self, oluşturulan nesnenin kendisine referans veren bir gelenektir (Python’da zorunlu bir anahtar kelime değildir, ancak yaygın olarak kullanılır). Bu sayede metot içinde nesnenin kendi niteliklerine erişebilir ve onları ayarlayabiliriz.
class Araba:
def __init__(self, marka, model, yil):
# self.marka, self.model, self.yil, bu nesneye ait niteliklerdir.
self.marka = marka
self.model = model
self.yil = yil
self.kilometre = 0 # Her araba başlangıçta 0 km'dir
Yukarıdaki örnekte, Araba sınıfının her yeni nesnesi oluşturulduğunda marka, model ve yil değerlerini alacak ve bu değerleri nesnenin kendi nitelikleri olarak saklayacaktır. kilometre niteliği ise varsayılan olarak 0 ile başlatılır.
Metotlar (Davranışlar)
Metotlar, bir sınıf içindeki fonksiyonlardır ve o sınıftan oluşturulan nesnelerin yapabileceği eylemleri veya davranışları tanımlar. Metotlar da self parametresini alarak nesnenin kendi niteliklerine erişebilir veya onları değiştirebilir.
class Araba:
def __init__(self, marka, model, yil):
self.marka = marka
self.model = model
self.yil = yil
self.kilometre = 0
def bilgi_goster(self):
"""Arabanın mevcut bilgilerini gösteren metot."""
print(f"Marka: {self.marka}, Model: {self.model}, Yıl: {self.yil}, Kilometre: {self.kilometre}")
def sur(self, mesafe):
"""Arabayı belirli bir mesafe sürer ve kilometreyi artırır."""
if mesafe > 0:
self.kilometre += mesafe
print(f"{self.model} {mesafe} km yol kat etti. Toplam kilometre: {self.kilometre}")
else:
print("Sürülecek mesafe pozitif olmalıdır.")
Burada bilgi_goster ve sur adında iki metot tanımladık. bilgi_goster metodu arabanın mevcut niteliklerini yazdırırken, sur metodu kilometre niteliğini günceller.
Nesne Oluşturma ve Kullanımı
Sınıfı tanımladıktan sonra, ondan somut nesneler oluşturabiliriz. Bu işleme “nesne örneklendirme” (instantiation) denir.
Nesne Örneklendirme (Instantiation)
Bir nesne oluşturmak için sınıf adını bir fonksiyon çağırır gibi kullanırız ve __init__ metodunun beklediği argümanları ( self hariç) parantez içinde göndeririz.
# Araba sınıfından iki farklı nesne oluşturma
araba1 = Araba("Toyota", "Corolla", 2020)
araba2 = Araba("Honda", "Civic", 2022)
print(f"Araba 1: {araba1.marka} {araba1.model}")
print(f"Araba 2: {araba2.marka} {araba2.model}")
Bu örnekte, araba1 ve araba2 artık Araba sınıfının bağımsız nesneleridir. Her birinin kendi marka, model, yil ve kilometre nitelikleri bulunur.
Niteliklere Erişme ve Metotları Çağırma
Bir nesnenin niteliklerine erişmek veya metotlarını çağırmak için “nokta” (.) operatörünü kullanırız.
# araba1'in niteliklerine erişme
print(f"Araba 1'in markası: {araba1.marka}")
print(f"Araba 1'in modeli: {araba1.model}")
print(f"Araba 1'in mevcut kilometresi: {araba1.kilometre}")
araba1'in metotlarını çağırma
araba1.bilgi_goster() # Marka: Toyota, Model: Corolla, Yıl: 2020, Kilometre: 0
araba1.sur(150) # Corolla 150 km yol kat etti. Toplam kilometre: 150
araba1.bilgi_goster() # Marka: Toyota, Model: Corolla, Yıl: 2020, Kilometre: 150
araba2'nin niteliklerine erişme ve metotlarını çağırma
araba2.bilgi_goster() # Marka: Honda, Model: Civic, Yıl: 2022, Kilometre: 0
araba2.sur(200) # Civic 200 km yol kat etti. Toplam kilometre: 200
araba2.sur(50) # Civic 50 km yol kat etti. Toplam kilometre: 250
araba2.bilgi_goster() # Marka: Honda, Model: Civic, Yıl: 2022, Kilometre: 250
Gördüğünüz gibi, araba1 ve araba2 nesneleri birbirlerinden bağımsız olarak kendi verilerini tutar ve kendi metotlarını çalıştırır.
Nesne Kimliği
Her nesne, bellekte benzersiz bir konuma sahiptir. Python’daki id() fonksiyonu ile bir nesnenin bellek adresini (kimliğini) görebiliriz. Farklı nesneler farklı kimliklere sahip olacaktır.
print(f"araba1'in kimliği: {id(araba1)}")
print(f"araba2'nin kimliği: {id(araba2)}")
araba3 = araba1 # araba3, araba1 ile aynı nesneye referans verir
print(f"araba3'ün kimliği: {id(araba3)}") # araba1 ile aynı kimlik
Pratik Bilgiler ve Gelişmiş Konular
Sınıflar ve nesneler hakkında temel bilgileri edindikten sonra, daha pratik ve kullanışlı bazı özelliklere göz atalım.
Sınıf Nitelikleri ve Örnek Nitelikleri Arasındaki Fark
Şimdiye kadar gördüğümüz marka, model, yil gibi nitelikler “örnek nitelikleri”dir; yani her nesneye özgüdür. Bir de “sınıf nitelikleri” vardır. Bunlar, sınıfın tüm örnekleri tarafından paylaşılan niteliklerdir ve doğrudan sınıfın içinde, metotların dışında tanımlanır.
class Araba:
tekerlek_sayisi = 4 # Bu bir sınıf niteliğidir, tüm arabalar için 4'tür.
kasa_tipleri = ["Sedan", "Hatchback", "SUV"] # Liste de olabilir
def __init__(self, marka, model, yil):
self.marka = marka
self.model = model
self.yil = yil
self.kilometre = 0
def bilgi_goster(self):
print(f"Marka: {self.marka}, Model: {self.model}, Yıl: {self.yil}, Kilometre: {self.kilometre}")
print(f"Tekerlek Sayısı: {Araba.tekerlek_sayisi}") # Sınıf niteliğine sınıf adıyla erişim
Nesne oluşturma
araba1 = Araba("Toyota", "Corolla", 2020)
araba2 = Araba("Honda", "Civic", 2022)
Sınıf niteliğine doğrudan sınıf üzerinden erişim
print(f"Tüm arabaların tekerlek sayısı: {Araba.tekerlek_sayisi}")
print(f"Araba 1'in tekerlek sayısı: {araba1.tekerlek_sayisi}") # Nesne üzerinden de erişilebilir
Sınıf niteliğini değiştirme (tüm örnekleri etkiler)
Araba.tekerlek_sayisi = 6 # Artık tüm arabalar 6 tekerlekli olarak algılanır
print(f"Araba 2'nin yeni tekerlek sayısı: {araba2.tekerlek_sayisi}")
Önemli not: Bir örnek üzerinden sınıf niteliğini değiştirirseniz, aslında o örneğe yeni bir örnek niteliği eklemiş olursunuz.
araba1.tekerlek_sayisi = 3 # Bu sadece araba1'e özel bir 'tekerlek_sayisi' niteliği ekler, sınıf niteliğini değiştirmez.
__str__ ve __repr__ Metotları
Python’da nesnelerin string temsilini özelleştirmek için __str__ ve __repr__ adında özel metotlar kullanırız.
* __str__: Nesnenin insan tarafından okunabilir string temsilini döndürür. print() fonksiyonu veya str() fonksiyonu çağrıldığında kullanılır.
* __repr__: Nesnenin resmi string temsilini döndürür. Geliştiricilerin hata ayıklama sırasında nesneyi anlamalarına yardımcı olur. Genellikle nesneyi yeniden oluşturmak için kullanılabilecek bir string döndürmesi beklenir.
class Araba:
def __init__(self, marka, model, yil):
self.marka = marka
self.model = model
self.yil = yil
self.kilometre = 0
def __str__(self):
"""İnsan tarafından okunabilir temsil."""
return f"{self.yil} model {self.marka} {self.model} ({self.kilometre} km)"
def __repr__(self):
"""Geliştirici dostu temsil."""
return f"Araba(marka='{self.marka}', model='{self.model}', yil={self.yil}, kilometre={self.kilometre})"
araba_test = Araba("Tesla", "Model 3", 2023)
print(araba_test) # __str__ metodunu çağırır
Çıktı: 2023 model Tesla Model 3 (0 km)
print(repr(araba_test)) # __repr__ metodunu çağırır
Çıktı: Araba(marka='Tesla', model='Model 3', yil=2023, kilometre=0)
Bir liste içindeki nesneler de __repr__ kullanır
arabalar_listesi = [araba_test, Araba("BMW", "X5", 2021)]
print(arabalar_listesi)
Çıktı: [Araba(marka='Tesla', model='Model 3', yil=2023, kilometre=0), Araba(marka='BMW', model='X5', yil=2021, kilometre=0)]
Bu metotları tanımlamak, kodunuzun çok daha okunabilir ve hata ayıklaması kolay olmasını sağlar.
Kapsülleme (Encapsulation) ve Erişim Belirleyiciler (Conventions)
Kapsülleme, verileri (nitelikleri) ve bu veriler üzerinde çalışan metotları bir araya getirerek bir “kapsül” içinde saklama prensibidir. Bu, nesnenin iç işleyişini dış dünyadan gizlemeye ve verilere doğrudan erişimi kısıtlamaya yardımcı olur.
Python’da diğer bazı dillerdeki gibi katı private veya public anahtar kelimeleri yoktur. Bunun yerine, geliştiriciler arasında belirli isimlendirme kuralları (conventions) kullanılır:
* Public (Genel): Normal nitelikler ve metotlar. Dışarıdan doğrudan erişilebilir. (self.nitelik, self.metot())
* Protected (Korumalı): Tek alt çizgi (_) ile başlayan nitelikler veya metotlar. Bu, geliştiricilere “bu bileşene dışarıdan doğrudan erişmeyin, dahili kullanım içindir” mesajını verir. Ancak teknik olarak erişim engellenmez.
* Private (Özel): İki alt çizgi (__) ile başlayan nitelikler veya metotlar. Python, isim karıştırma (name mangling) yaparak bu bileşenlerin dışarıdan doğrudan erişimini daha zor hale getirir. Ancak yine de tamamen gizli değildir.
Çoğu durumda tek alt çizgi kuralı yeterlidir ve Python topluluğu tarafından yaygın olarak benimsenir.
class BankaHesabi:
def __init__(self, hesap_sahibi, bakiye):
self.hesap_sahibi = hesap_sahibi
self._bakiye = bakiye # İç kullanım için olduğu belirtilen korumalı nitelik
def para_yatir(self, miktar):
if miktar > 0:
self._bakiye += miktar
print(f"{miktar} TL yatırıldı. Yeni bakiye: {self._bakiye} TL")
else:
print("Yatırılacak miktar pozitif olmalıdır.")
def para_cek(self, miktar):
if miktar > 0 and self._bakiye >= miktar:
self._bakiye -= miktar
print(f"{miktar} TL çekildi. Yeni bakiye: {self._bakiye} TL")
else:
print("Geçersiz miktar veya yetersiz bakiye.")
def bakiye_sorgula(self):
return self._bakiye
hesap = BankaHesabi("Ali Yılmaz", 1000)
print(f"Hesap Sahibi: {hesap.hesap_sahibi}")
print(hesap._bakiye) # Kural gereği doğrudan erişimden kaçınılır, ancak teknik olarak mümkün
hesap.para_yatir(500)
hesap.para_cek(200)
print(f"Güncel Bakiye: {hesap.bakiye_sorgula()} TL")
Bu örnekte _bakiye niteliği, hesabın bakiyesinin doğrudan dışarıdan manipüle edilmesi yerine, para_yatir ve para_cek metotları aracılığıyla güvenli bir şekilde değiştirilmesi gerektiğini belirtir.
Neden Sınıflar Kullanmalıyız? (Gerçek Dünya Uygulamaları)
Sınıflar, karmaşık sistemleri modellemek ve kodunuzu daha düzenli hale getirmek için vazgeçilmezdir:
* Modelleme: Gerçek dünyadaki varlıkları (Kullanıcı, Ürün, Sipariş, Öğrenci, Kitap vb.) programatik olarak temsil etmek.
* Kod Yeniden Kullanımı: Bir sınıfı bir kez tanımladıktan sonra, ondan istediğiniz kadar nesne oluşturabilir ve aynı mantığı tekrar tekrar kullanabilirsiniz.
* Organizasyon ve Modülerlik: İlgili verileri ve fonksiyonları tek bir birim (sınıf) altında toplar, bu da kodun daha kolay anlaşılmasını ve yönetilmesini sağlar.
* Bakım Kolaylığı: Bir özelliğin değişmesi gerektiğinde, sadece ilgili sınıfı güncelleyerek bu değişikliği tüm nesnelere yansıtabilirsiniz.
Sonuç ve Sıkça Sorulan Sorular
Bu rehberde, Python’da sınıfların nasıl tanımlandığını, __init__ metodunun, niteliklerin ve metotların nasıl kullanıldığını öğrendiniz. Ayrıca nesnelerin nasıl oluşturulacağını, niteliklerine ve metotlarına nasıl erişileceğini, sınıf ve örnek nitelikleri arasındaki farkı, __str__ ve __repr__ metotlarını ve kapsülleme prensiplerini inceledik. Nesne yönelimli programlama, Python’da daha güçlü, esnek ve sürdürülebilir uygulamalar geliştirmenin anahtarıdır.
Sıkça Sorulan Sorular (SSS)
- S: Sınıf ile nesne arasındaki fark nedir?
- C: Sınıf, bir nesnenin nasıl olması gerektiğini tanımlayan bir plan veya şablondur. Nesne ise bu plandan oluşturulmuş, bellekte yer kaplayan somut bir örnektir. Bir sınıftan birden fazla nesne oluşturulabilir.
- S:
selfanahtar kelimesi ne işe yarar? - C:
self, bir sınıf metodunun ilk parametresidir ve o metodu çağıran nesnenin kendisine referans verir. Bu sayede metot içinde nesnenin kendi niteliklerine erişilebilir ve metotlar nesneye özgü işlemler yapabilir. - S:
__init__metodu neden önemlidir? - C:
__init__yapıcı metottur ve bir sınıftan yeni bir nesne oluşturulduğunda otomatik olarak çağrılır. Nesnenin başlangıç durumunu ayarlamak, yani temel niteliklerini ilk değerleriyle başlatmak için kullanılır. - S: Bir sınıfın birden fazla
__init__metodu olabilir mi? - C: Hayır, Python’da diğer bazı dillerdeki gibi metod aşırı yükleme (method overloading) doğrudan desteklenmez. Bir sınıfta sadece bir
__init__metodu tanımlanabilir. Farklı başlatma senaryoları için genellikle varsayılan argümanlar veya sınıf metotları (@classmethod) kullanılır. - S:
passanahtar kelimesi bir sınıf tanımında ne anlama gelir? - C:
pass, Python’da “hiçbir şey yapma” anlamına gelen bir yer tutucudur. Boş bir sınıf veya metot tanımlamanız gerektiğinde, ancak Python’ın girintili bloklar için en az bir ifade beklediği durumlarda kullanılır. Kodunuzu daha sonra doldurmak üzere taslak olarak bırakmak için kullanışlıdır.
