# Python Sınıflar

Python sınıflar (class) konusunu işliyoruz.

Python nesne yönelimli bir dildir ve gücünü bu sayede katlayarak bizlere ulaşır. Python programlama dilinde neredeyse her şey bir objedir. Peki sınıf ve obje nedir?

Programlama dillerinde sınıf ; içerisinde değişken ve fonksiyonları barındıran, yeri geldiğinde bunları kullandıran taslaklardır. Bir örnek vererek bu tanımı güçlendirelim.

En klasik örnek üzerinden yürürsek, bir okulunuz var ve okulda şubelere, her şubede ise onlarca öğrenciye sahipsiniz. Bu elemanlarla bir yazılım hazırlayacak olursak farketmişsinizdir ki şimdiye kadar gördüğümüz dersler bu konuda yetersiz kalacaktır. Çünkü binlerce öğrenci için binlerce değişken oluşturmamız mı gerekecek? Veya her öğrencinin farklı özelliklerini, şube adını, veli bilgilerini vs nerede nasıl tutacağız? Ortada büyük bir kaos var maalesef Elimizde ki problemin kaynağını öğrendik. Büyük verilerle oynarken her üye ve bu üyenin özellikleri için bir değişken oluşturursak binlerce değişkenle işin içinden çıkamayız. İşte tam burada da Python sınıf kullanımı imdadımıza yetişiyor. Eğer elimizde bir sınıfın ve öğrencinin nasıl olacağına dair bir taslak olursa, bu sınıflardan istediğimiz kadar elemanı rahatlıkla oluşturabilir ve sınıfın özelliklerini üyelere yani objelere (nesnelere) atayabiliriz. Görselde gördüğünüz üzere basit bir öğrenci taslağı hazırladık. Bu taslakta öğrencilerin alacağı değişken özelliklerini ve sahip olacakları fonksiyonları oluşturduk. Artık bu taslağa bağlı olarak istediğimiz kadar öğrenci yani nesne (obje) oluşturabiliriz.

1. Class Oluşturma

Python programlama dilinde bir sınıf oluşturabilmek için class terimini kullanırız.

class ogrenci:
pass

Evet işte bu kadar. En temel haliyle elimizde artık bir sınıfımız var. Şimdi ise tüm öğrencilerde olacak ortak sınıf değişkenlerimizi tanımlayalım.

class ogrenci:
isim = None
yas = None
sinif = None
devamsizlik = 0

Burada ise sınıf değişkenlerimizi oluşturduk fakat bunları yaparken çoğu değişkene herhangi bir değer atamadık. İsteseydik burada devamsizlik değişkeninde olduğu gibi öntanımlı bir değer atayarak bu sınıf değişkenlerinin ilk başta bir değere sahip olmasını sağlayabilirdik. Sırada sınıfımıza fonksiyonel yetenek kazandıracak methodları ekleme var.

class ogrenci:
isim = None
yas = None
def selamVer():
print("Merhaba")

Artık çok basit bir sınıfımız olduğuna göre ilk testlerimizi yapalım.

2. Class Değişken ve Fonksiyonlarını Çalıştırma

İlk başlığımızda basit bir sınıfın nasıl oluşturulduğunu gördük. Nesne ve diğer sınıf yeteneklerine ulaşmadan önce basit yoldan sınıf elemanlarına nasıl erişebileceğimizi görelim. Elimizdeki bir sınıfın değişken ve fonksiyonlarına ulaşmak için sınıf adını yazdıktan sonra ulaşmak istediğimiz elementi seçmemiz gerekiyor.

class ogrenci:
isim = "-"
yas = 10
def selamVer():
print("Merhaba")
print(ogrenci.isim) # -
print(ogrenci.yas) # 10
ogrenci.selamVer() # Merhaba

Örnekte gördüğünüz gibi basit bir sınıfın elemanlarına basit bir yöntemle ulaştık. Şimdi ise sınıf devamsizlik değişkenini her çağırdığımızda bir artıran fonksiyonu yazalım.

class ogrenci:
isim = "-"
devamsizlik = 0
def devamsizlikEkle():
ogrenci.devamsizlik += 1
print(ogrenci.devamsizlik) # 0
ogrenci.devamsizlikEkle() # Merhaba
print(ogrenci.devamsizlik) # 1

Buradaki örnekte ise sınıf devamsizlik değişkenimizin sınıf dışından nasıl artırılacağını gördük.

Peki bu yöntemleri normal bir fonksiyondan ayıran özellikler nerede? Özellikle de bu taslağı farklı farklı nesneler üzerinde kullanacağımız objeler nerede? Sanki bir şeyler eksik gibi Şimdiye kadar gördüğümüz kısımlar bir sınıfın en temelidir ve iskeletinin nasıl olduğunu anlatır. Fakat bu haliyle sınıfları sınıf gibi kullanamazsınız. Çünkü elimizde halen objeler yok ve yaptığımız her işlem sınıfın kendi değişkenleri üzerinde gerçekleşti. Öyleyse şimdi hemen nesne kullanımını öğrenelim ve asıl sınıf dersine başlayalım.

3. Nesne (Obje) Oluşturma

Sınıflar (classes) konusunun kullanılmasının asıl amacı değişken yığınlarıyla uğraşmayıp bir taslak oluşturmak ve bu taslağı tüm değişkenlerimize uygulayabilmektir. Böylece hem bir kargaşadan kurtuluruz hemde daha profesyonel bir kod yazabiliriz. Python programlama dilinde yazdığınız bir sınıftan nesneler oluşturmak için değişkeninize sınıf ismini fonksiyon olarak atamalısınız. Örnekleri hemen görelim.

class ogrenci:
isim = None
yas = None
o1 = ogrenci()
o2 = ogrenci()

Artık elimizde ogrenci sınıfından türemiş iki adet nesnemiz (objemiz) oldu. Öyleyse bu nesnelerimiz için sınıfımızın isim değişkenine değer atamakla başlayalım.

class ogrenci:
isim = None
yas = None
o1 = ogrenci()
o2 = ogrenci()
o1.isim = "Yunus"
o2.isim = "İsmet"
print(o1.isim) # Yunus
print(o2.isim) # İsmet

Nesnelerimizle ilk temasımızı kurduk. Artık istediğiniz sayıda obje oluşturabilirsiniz. Hatta basit bir fonksiyonla her öğrencinin selam vermesini de sağlayabiliriz.

class ogrenci:
isim = None
yas = None
devamsizlik = 0
def selamVer():
print("Merhaba !")
o1 = ogrenci()
o2 = ogrenci()
o1.selamVer() # Merhaba !
o2.selamVer() # Merhaba !

Bu örnekte her öğrencinin kendi ismiyle selam vermesini istersek peki nasıl yapabiliriz?

class ogrenci:
isim = "---"
devamsizlik = 0
def selamVer():
print("Merhaba, ben " + ogrenci.isim)
o1 = ogrenci()
o1.isim = "Yunus"
ogrenci.selamVer() # Merhaba, ben ---
o1.selamVer() # !!!!!! HATA !!!!!!

Bu da nedir? Neden böyle bir hata aldık ki? Peki şimdi sınıfımıza devamsızlık değişkenini ve her çağrıldığında öğrenci nesnesinin devamsızlık sayısını bir artıracak fonksiyonu yazsak yine hata alır mıyız acaba?

class ogrenci:
isim = None
devamsizlik = 0
def devamsizlikEkle():
ogrenci.devamsizlik += 1
o1 = ogrenci()
print(o1.devamsizlik) # 0
ogrenci.devamsizlikEkle()
print(o1.devamsizlik) # 1
o1.devamsizlikEkle() # !!!!!! HATA !!!!!!

Sanırım bir terslik var. Objelerimiz sınıf değişkenlerine erişebiliyor ama onları sınıf değişkenleri üzerinden değiştiremiyor Şimdi bunun sebebini yeni başlığımız altında öğrenelim. (Cevabı konu sonunda)

5. init() Fonksiyonu ve self Terimi Kullanımı

Şu ana kadar sınıfları nasıl oluşturup kullanacağımızı ve bu sınıflara ait değişkenleri nasıl tanımlayacağımızı gördük. Bu cümledeki sınıflara ait değişkenler kısmı önemlidir çünkü oluşturduğumuz tüm değişkenler nesnelerimize ait değil, sınıfımıza aittir. Hatırlayalım hemen;

class ogrenci:
isim = "-"

Bu örnekte isim değişkeni ogrenci sınıfının bir değişkenidir. Yani bir nesne (obje) oluşturduğunuzda bu nesne üzerinden ilgili değişkene ulaşabilir ve değerini değiştirebilirsiniz fakat yaptığınız değişiklikten tüm nesneler etkilenir.

class ogrenci:
yas = 10
o1 = ogrenci()
o2 = ogrenci()
ogrenci.yas = 20
print(ogrenci.yas) # 20
print(o1.yas) # 20
print(o2.yas) # 20

Örnekte gördüğünüz gibi o1 ve o2 olmak üzere iki adet nesne oluşturduk ve sadece sınıf yas değişkene ulaşarak değerini değiştirmemize rağmen her iki nesnede de bu değer değişti. Çünkü yas değişkeni bu nesnelerin kendi değişkeni değil, sınıfın kendi değişkenidir ve nesneler ilgili değişkenlere değer atamadığı sürece tüm değişikliklerden etkilenir.

Bu şekilde sınıfın kendi değişkenlerini kullanarak objeleri etkili şekilde kullanmayız. Çünkü elimizdeki her objenin (örneğimizde yer alan öğrencilerin) kendine has değişken değerleri olmalıdır ve diğer nesnelerden etkilenmemelidir.

init(self) fonksiyonu Python programlama dilinde bir nesne oluşturulduğunda çalışan özel bir elemandır. Nesne oluşturur oluşturmaz çalışır ve self ile içinde oluşturduğumuz değişkenleri nesneye atar. Böylece her nesnenin kendine has değişken değerleri olmuş olur. Bu kısmı örneklerle adım adım anlatalım.

class ogrenci():
yas = 0
print("Merhaba")
# Merhaba

Bu kodu çalıştırır çalıştırmaz ekranda "Merhaba" yazısını görürsünüz. Çünkü sınıflar kodlanır kodlanmaz kendi değişkenlerini ve fonksiyonlarını tanımlar. Burada da ogrenci sınıfına ait olan print komutu çalıştı.

class ogrenci():
def __init__(self):
print("Merhaba")

Bu kodu çalıştırdığınızda ise ekranda hiçbir çıktı göremezsiniz. Çünkü biraz yukarıda init(self) fonksiyonunun sınıf örneklenir örneklenmez, yani sınıftan bir nesne(obje) oluşturduğumuzda çalıştığını öğrendik. Öyleyse kodumuzu şu şekilde düzenlersek ekranda çıktı görebiliriz.

class ogrenci():
def __init__(self):
print("Merhaba")
o1 = ogrenci()
# Merhaba

Gördüğünüz gibi o1 isimli nesne oluşturduğumuza ilklendirme fonksiyonumuz çalıştı ve ekrana "Merhaba" yazdı. Öyleyse sınıf değişkenlerimize ek olarak nesnelerimize ait olacak değişkenlerimizi de tanımlayalım ve böylece her nesneye özel değişken değeri atayabilelim. Sınıf değişkenleri ise tüm sınıf genelinde değişmeyecek sabit değerler için kullanılabilir. Örneğin pi sayısı gibi.

class ogrenci():
def __init__(self):
self.isim = "YOK"
self.yas = 0
o1 = ogrenci()
o2 = ogrenci()
print(o1.isim + " - " + str(o1.yas)) # YOK - 0
print(o2.isim + " - " + str(o2.yas)) # YOK - 0

Burada gördüğünüz gibi nesnemiz oluşturulduğunda __init__(self) fonksiyonu objeler oluşturulduğu anda çalıştı ve nesnelerimize ilk değerlerini atadı. self değişkeni ise nesnelerimizi temsil ederek (o1 ve o2) ilgili nesne üzerinde işlem yapılmasını sağladı.

class ogrenci():
def __init__(self):
self.isim = None
self.yas = None
o1 = ogrenci()
o2 = ogrenci()
o1.isim = "Yunus"
o2.isim = "İsmet"
o1.yas = 10
o2.yas = 15
print(o1.isim + " - " + str(o1.yas)) # Yunus - 10
print(o2.isim + " - " + str(o2.yas)) # İsmet - 15

Yine bu örnekte olduğu gibi her nesne self ile kendi değişkenlerine ulaşarak değer atamalarını gerçekleştirdi.

self terimi nesneyi temsil eder (o1.isim) , (o2.isim) gibi doğru nesne üzerinde çalışılmasını sağlar. Siz bu terimi herhangi bir isimle değiştirerek (örneğin self yerine elma kullanarak) kullanabilirsiniz ama programlama da self kelimesi kalıplaşmıştır.

Bu başlığa son bir örnek daha verelim ve sınıf değişkenleri ile nesne değişkenleri arasındaki farkı anlayalım.

class ogrenci():
yas = 10
def __init__(self):
self.yas = 20
o1 = ogrenci()
print(ogrenci.yas) # 10
print(o1.yas) # 20

Gördüğünüz gibi obje oluşturulduğunda kendi yas değişkenini tanımladı ve sınıf değişkeninden bağımsız olarak kullandı.

class ogrenci():
def selamVer():
print("Merhaba")
def selamVer(self):
print("Merhaba !!")
o1 = ogrenci()
o1.selamVer() # Merhaba !!

Aynı şekilde selamVer() fonksiyonunu nesne üzerinden çalıştırdığımızda kendini temsil eden self teriminin olduğu fonksiyonu görerek ilkini değil ikinci fonksiyonu çalıştırdı.

Eğer bir nesne değişkeniyle çalışıyorsanız değişkenin başına self terimini getirmeyi unutmayınız.

6. Ön Tanımlı Değerlerle Obje Oluşturmak

Bir nesneyi isterseniz öntanımlı değerlerle oluşturabilirsiniz. Böylece daha sonrasında tek tek nesne değerleri atamak zorunda kalmazsınız.

class ogrenci:
def __init__(armut, isim, yas):
armut.isim = isim
armut.yas = yas
def selamVer(armut):
print("Merhaba, ben " + armut.isim)
o1 = ogrenci("İsmet", 24)
o1.selamVer()

self terimi yerine istediğiniz bir kelime ( armut gibi) kullanabilirsiniz demiştik. Fakat siz self kullanmakta ısrarcı olun çünkü Python programlama dilinde self terimi kalıplaşmıştır.

7. Nesne ve Obje Değişkeni Silme

Sınıfımızı örneklendirerek istediğimiz kadar nesne oluşturabileceğimizi şimdiye kadar gördük. Peki oluşturduğumuz nesneleri nasıl silebiliriz? Elimizdeki nesneleri del terimi ile rahatlıkla silebiliriz.

class ogrenci():
yas = 10
o1 = ogrenci()
del o1
print(o1.yas) # !!! HATA !!!

Bu örnekte hata alırız çünkü nesnemizi artık mevcut olmadığı için kullanamayız. Aynı şekilde yine del terimiyle nesnelerimize ait değişkenleri de silebiliriz.

class ogrenci():
def __init__(self):
self.yas = 20
o1 = ogrenci()
o2 = ogrenci()
del o2.yas
print(o1.yas) # 20
print(o2.yas) # !!! HATA !!!

Burada ise o2 objesinin yas değişkenini sildiğimiz için artık ulaşamıyoruz.

Soru Cevabı Üçüncü başlığımız olan Nesne (Obje) Oluşturma'nın sonundaki sorumuzun cevabına gelmeden önce sorunumuzu bir hatırlayalım: Oluşturduğumuz sınıf değişkenine nesneler üzerinden bir değer atamadan direkt sınıf değişkeni üzerinden değer atama yaptığımızda tüm nesnelerdeki değerler değişiyor ama önce nesne üzerinden sınıf değişkenine değer atadığımızda sınıf değişkeni üzerinden değer atasak bile değer nesnelerde değişmiyor. Sebebi Nedir?

class ogrenci:
yas = 10
o1 = ogrenci()
o2 = ogrenci()
print(o1.yas) # 10
print(o2.yas) # 10

Yukarıdaki örnekte objeler yas sınıf değişkeninin değerini öntanımlı değerinden alır.

class ogrenci:
yas = 10
o1 = ogrenci()
o2 = ogrenci()
ogrenci.yas = 20
print(o1.yas) # 20
print(o2.yas) # 20
print(ogrenci.yas) # 20

Ama yukarıdaki gibi nesnenin yas değişkenine değer atamadan önce sınıf değişkeni üzerinden değer atarsak bu değişiklik tüm nesneleri etkiler.

class ogrenci:
yas = 10
o1 = ogrenci()
o2 = ogrenci()
o1.yas = 30
ogrenci.yas = 20
print(o1.yas) # 30
print(o2.yas) # 20
print(ogrenci.yas) # 20

Fakat yukarıdaki örnekte olduğu gibi önce nesnenin yas değişkenini değiştirirsek artık o nesne için ilgili değer sınıd değişkeni üzerinden değiştirilemez.

Bunun sebebi yas değişkeninin nesnelerin değil sınıfın bir değişkeni olmasıdır. Bu nedenle bu değişken üzerinde yaptığımız bir değişiklik tüm nesneleri etkiler. Fakat siz bir nesne üzerinden bu değere ulaşıp kullandığınızda ilgili değişken ayriyeten objeye has bir değişken olarakta saklanır.

Bu sebeplerden dolayı pi sayısı, firma adı, okul adı gibi değişmeyecek veya değişince herkeste değişmesini istediğiniz değerleri sınıf değişkeni olarak tutabilirsiniz. Fakat her nesne için değişecek değerleri init(self) fonksiyonu ile oluşturmanızda fayda vardır. Aksi halde bu başlıktaki sorun programınızın düzensiz çalışmasına sebep olabilir.