Python’da “Sınıf (Class)” Oluşturma ve “Nesne (Object)” Kullanımı
Bu yazımızda, Python’da nasıl sınıf (Class) oluşturulacağını ve bu sınıflardan nasıl nesne (Object) türetileceğini detaylı bir şekilde ele alacağız. Özellikle nesne yönelimli programlamanın temel kavramlarından olan “sınıf ve nesne” yapıları, uygulama geliştirme süreçlerinde oldukça kritik bir rol oynamaktadır. Bu yüzden oldukça önemlidir.
Python’da “Sınıf (Class)” Oluşturmak
Python’da sınıflar, belirli bir nesne grubunun özelliklerini ve işlevlerini modellemek için kullanılır. Bu yönüyle sınıfı, şablon görevi gören taslaklar (blueprint) olarak tanımlayabiliriz. Bu taslaklardan faydalanarak oluşturduğumuz yapılarak ise nesne adı verilir.
Sınıfları ve nesneleri anlamak için onları yemek tarifleriyle hazırlanan yemeklere benzetebiliriz. Sınıf (class), bir yemek tarifi gibidir; hangi malzemelerin kullanılacağını, ne sıra ile ekleneceğini ve nasıl bir işlem yapılacağını belirtir. Ancak bu tarifteki ölçülere ve adımlara ne kadar sadık kalırsak kalalım, her seferinde hazırladığımız yemek bir öncekine göre biraz farklı olabilir. İşte bu farklılıkları temsil eden her bir yemek, birer nesne (object) olup, aynı sınıftan (tariften) türetilmiştir. Yani, sınıf soyut bir yapıyı temsil ederken, nesneler bu yapıya somut bir form kazandırır ve her biri kendi benzersiz özellikleriyle ayrılır.
Python’da sınıf oluşturmak için, kelimenin İngilizce anlamı olan, class
anahtar kelimesinden faydalanırız. Yalnız diğer anahtar kelimelerden farklı olarak adlandırmaya her zaman büyük bir harfle başlamamız gerekir ve adlandırmayı “PascalCase” şeklinde yapmamız gerekir. Örneğin:
1 2 |
class ArabaModeli: pass |
Yeri gelmişken örneğimizde de kullandığımız pass
anahtar kelimesinin ne anlama geldiğini ve niye kullanıldığını açıklayalım:
Bir sınıf veya fonksiyon tanımı oluşturduğunuzda, Python’da bu blokların girintili bir şekilde içeriğe sahip olması beklenir. Ancak bazen içerik yazmadan sadece sınıf veya fonksiyonun iskeletini (yapısını) oluşturmak istersiniz. İşte bu gibi durumlarda pass
kelimesini kullanarak Python’a “bu blok için şimdilik hiçbir şey yapma” mesajını veririz.
“Peki, hiç bir şey yazmasam olmaz mıydı? Yani ille de “pass” yazmak zorunda mıyım?” gibi bir soru sorabilirisiniz. Evet zorundayız. Python, sınıf bloğu içerisinde herhangi bir içerik bulamadığında eğer pass
yazılmamışsa “IndentationError: expected an indented block
” hatasını verecektir.
Bir sınıfın içerisine, o sınıfı tanımlayan özellikler (nitelikler) ve bu özellikler üzerinde işlem yapabilecek fonksiyonlar (metodlar) ekleyebiliriz. Nitelikler, sınıfın özelliklerini veya durumunu temsil ederken; metodlar, sınıfın ne tür eylemler gerçekleştirebileceğini tanımlar. Ayrıca, sınıfın başlangıçta hangi değerlere sahip olacağını belirtmek için __init__
adında özel bir başlatma metodunu da tanımlayabiliriz. Bu yapı, sınıfımızın bir nesnesi oluşturulduğunda otomatik olarak çağrılır. Bunların yanı sıra, sınıf içerisinde sınıf değişkenleri, sınıf metodları ve statik metodlar gibi daha ileri seviye özellikleri de ekleyebiliriz.
Özellik ve Metotlar
İşe ilk olarak aşina olduğumuz değişken (variable) ve fonksiyon (function) kavramlarından başlayalım. Python’da sınıf içerisinde tanımladığımız değişkenlere “nitelik” ya da “özellik” (attribute) adını veririz. Bu nitelikler, sınıfın ya da sınıfın örneklerinin durumunu ve sahip olduğu bilgileri temsil eder. Örneğin, bir Kisi
sınıfının içerisinde ad
ve yas
gibi nitelikler bulunabilir.
Bunun yanında, sınıf içerisinde tanımladığımız fonksiyonlara ise “metod” (method) diyoruz. Metodlar, sınıfın ya da sınıfın örneklerinin gerçekleştirebileceği eylemleri veya işlemleri tanımlar. Yine Kisi
sınıfını düşünerek, bu sınıf içerisinde kişinin selam vermesini sağlayan selam_ver
adlı bir metod tanımlanabilir. Bu terminoloji, nesne yönelimli programlamada daha tutarlı ve anlamlı bir dil oluşturmak için kullanılır.
- değişken (variable) ➡️ nitelik/özellik (attribute)
- fonksiyon (function) ➡️ metot/yöntem (method)
Sınıf içerisindeki değişkenler ve fonksiyonlar için farklı adlandırmalara sahip olmamızın temel nedeni, nesne yönelimli programlamanın (OOP) sunduğu yapısal ve soyutlamaya dayalı yaklaşımı uyguladığımızı daha iyi ifade etmektir.
Haliyle, nitelikler (attributes), bir sınıfın veya onun örneklerinin sahip olduğu özellikleri veya durumu temsil eder. Örneğin, bir Araba
sınıfı için nitelikler renk, marka veya model olabilir. Buna karşılık metotlar (methods) ise bu nitelikler üzerinde işlem yapar veya sınıfın (veya onun örneklerinin) nasıl davrandığını tanımlar. Yine Araba
sınıfını örnek alacak olursak, hizlan
, yavasla
veya korna_cal
gibi metotlar bu sınıfın davranışlarını temsil eder. Örneğin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class Araba: # Bu kısımdaki değişkenler sınıfın nitelikleri (özellikleri) olarak adlandırılır. marka = "" model = "" renk = "" hiz = 0 # Aşağıdaki fonksiyonlar sınıfın metotlarıdır (davranışları). def hizlan(self, miktar): self.hiz += miktar return f"Şu anki hız: {self.hiz} km/sa" def yavasla(self, miktar): self.hiz -= miktar if self.hiz < 0: self.hiz = 0 return f"Şu anki hız: {self.hiz} km/sa" def korna_cal(self): return f"{self.marka} {self.model} korna çalıyor!" benim_arabam = Araba() benim_arabam.marka = "Toyota" benim_arabam.model = "Corolla" benim_arabam.renk = "Mavi" print(benim_arabam.korna_cal()) # Toyota Corolla korna çalıyor! |
Sınıfımızdaki marka
, model
, renk
ve hiz
nitelikleri (veya özellikleri) temsil eder. Bunlar sınıfın durumunu veya değerlerini saklar. Buna karşılık hizlan
, yavasla
ve korna_cal
sınıfımızın metotlarıdır. Bunlar sınıfın (veya onun örneklerinin) nasıl davrandığını belirtir ve genellikle bir işlevsellik sağlar. Kodu çalıştırmak için: Trinket
“self” Kavramı
Yukarıdaki örneğimzde fark edileceği üzere “self” adında bir kavram görülmektedir. Python’da, self terimi birçok kişi için ilk başta karmaşık gelebilir ama aslında oldukça basit bir konsepti ifade eder.
Bir sınıf içerisinde, metotların (yani o sınıf içerisinde tanımlanan fonksiyonların) o sınıfın niteliklerine (yani değişkenlerine) veya diğer metotlarına erişebilmesi için bir araca ihtiyaçları vardır. İşte bu araç, “self” kelimesidir.
Örneğimizdeki gibi bir “Araba” sınıfınız olduğunu düşünelim ve bu sınıf içerisinde “marka” adında bir nitelik (değişken) ve “korna_cal” adında bir metot (fonksiyon) tanımladınız. “korna_cal” fonksiyonunun, “marka” değişkenini kullanarak hangi arabanın korna çaldığını söylemesini istiyorsunuz. İşte bu fonksiyonun, “marka” değişkenine erişebilmesi için “self” kelimesini kullanması gerekiyor. “self.marka” şeklinde bir ifadeyle, “şu anki arabanın markası”na erişim sağlanır.
Bu yüzden, “self”, sınıf içerisinde tanımlanan metotların, o sınıfın niteliklerine veya diğer metotlarına erişmelerini sağlayan bir anahtardır. Yani kabaca; sınıfın içindekileri, sınıfın içindekilere bağlayan mekanizmadır.
“__init__ ()” Fonksiyonu
Verdiğimiz örneği ele alalım. Burada bir Araba
nesnesi oluşturuyoruz ve ardından bu arabaya marka, model ve renk atıyoruz. Fakat bu yaklaşım, özellikle daha büyük projelerde veya çok sayıda araba nesnesi oluşturduğumuzda bizi yorabilir. Her bir araba için marka, model ve renk bilgisini tek tek giriyor olmamız, hem zamanımızı alacak hem de bazen unutkanlıkla bazı bilgileri atlamamıza yol açabilir.
Ayrıca, bir arabayı temsil eden bu sınıfta, belki her arabanın başlangıçta belirli özelliklere sahip olmasını isteyebiliriz. Örneğin, her arabayı oluşturduğumuzda hızının sıfır olmasını garantileyebiliriz.
Bu tür bir otomasyonu ve başlangıç değerlerini belirlemeyi kolaylaştırmak için, __init__
fonksiyonunu kullanmaya başlarız. Bu fonksiyon, bir Araba
nesnesi oluşturulduğunda otomatik olarak çağrılır. Yani, bir Araba
nesnesi oluşturduğumuz anda, bu arabaya marka, model, renk ve diğer başlangıç değerlerini hemen atayabiliriz.
Bu yaklaşım sayesinde, her yeni nesne oluşturduğumuzda tekrar tekrar aynı bilgileri girmekten kurtuluruz ve nesnelerimizin daha tutarlı bir şekilde başlatılmasını sağlarız. Örneğimizi yeniden yazalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Araba: def __init__(self, marka, model, renk, hiz=0): self.marka = marka self.model = model self.renk = renk self.hiz = hiz def hizlan(self, miktar): self.hiz += miktar return f"Şu anki hız: {self.hiz} km/sa" def yavasla(self, miktar): self.hiz -= miktar if self.hiz < 0: self.hiz = 0 return f"Şu anki hız: {self.hiz} km/sa" def korna_cal(self): return f"{self.marka} {self.model} korna çalıyor!" benim_arabam = Araba("Toyota", "Corolla", "Mavi") print(benim_arabam.korna_cal()) # Toyota Corolla korna çalıyor! |
Bu yaklaşımla, Araba
sınıfından bir nesne oluştururken doğrudan marka, model ve renk bilgisini vererek nesneyi başlatıyoruz. Bu, kodun daha okunabilir olmasını sağlar ve potansiyel olarak hata yapma riskimizi azaltır. Ayrıca, hiz
özelliğini varsayılan olarak 0 değeri ile başlattık, böylece bu değeri her oluşturma işlemi sırasında belirtmemiz gerekmiyor.
Bu arada, yeri gelmişken self kelimesi yerine farklı adlandırmalar da yapılabildiğini söylemek isterim. Aşağıdaki örnekte bunu self yerine oyuncu yazarak gerçekleştirdik:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Python'da Sınıf (Class) ve Nesne (Object) Yapısı class Futbolcu: def __init__(oyuncu, ad, soyad, yas): # (ad, soyad, yas) = ("Lionel", "Messi", 36) oyuncu.ad = ad oyuncu.soyad = soyad oyuncu.yas = yas futbolcu1 = Futbolcu("Lionel", "Messi", 36) print(futbolcu1.ad) print(futbolcu1.soyad) print(futbolcu1.yas) |
Python’da “self” kelimesi, sadece bir gelenek olarak kullanılır ve pekala başka herhangi bir kelimeyle değiştirilebilir. Ancak, “self” kelimesi Python topluluğunda yaygın olarak kabul görmüştür, bu yüzden bunu kullanmaya özen gösterin. Kodu çalıştırmak için: Trinket
Python’da Nesne Özelliklerini Değiştirmek
=
işareti ile kolayca değiştirilmek istenen nesne özellikleri değiştirilebilir:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Futbolcu: def __init__(self, ad, soyad, yas): self.ad = ad self.soyad = soyad self.yas = yas # Oyuncu oluşturalım futbolcu1 = Futbolcu("Lionel", "Messi", 36) # Şimdi bu oyuncunun adını değiştirelim futbolcu1.ad = "Cristiano" print(futbolcu1.ad) # Bu artık "Cristiano" olarak çıktı verecektir |
Bu örnekte, “Lionel” olarak tanımlanan futbolcu1
adlı nesnenin ad
niteliğini, “=” işareti kullanarak “Cristiano” olarak değiştirdik. Bu, obje yönelimli programlamanın esnekliğini gösteren bir özelliktir. Ancak, doğrudan niteliklere erişip değiştirme işlemi bazen istenmeyen sonuçlara yol açabilir. Bu tür olası problemleri önlemek için bazen “private” (özel) nitelikler veya “getter” ve “setter” metotları kullanılır. Ancak bu, başka bir konunun detayıdır. Kodu çalıştırmak için: Trinket
Nesnenin Bir Özelliğini Ya Da Nesneyi Silmek
Eğer bir nesnenin belirli bir niteliğini silmek istiyorsak, bu niteliğe del
anahtar kelimesi ile erişebiliriz. Bakınız:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Futbolcu: def __init__(self, ad, soyad, yas): self.ad = ad self.soyad = soyad self.yas = yas futbolcu1 = Futbolcu("Lionel", "Messi", 36) # soyad niteliğini silelim del futbolcu1.soyad # Şimdi soyad'a erişmeye çalışırsak bir hata alırız # print(futbolcu1.soyad) # Bu satır hata verir |
Eğer bir nesneyi komple silmek ve bellekten kaldırmak istiyorsak, bu sefer de nesneye del
anahtar kelimesi ile erişiriz. Bakınız:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Futbolcu: def __init__(self, ad, soyad, yas): self.ad = ad self.soyad = soyad self.yas = yas futbolcu1 = Futbolcu("Lionel", "Messi", 36) del futbolcu1 # Şimdi futbolcu1 nesnesine erişmeye çalışırsak bir hata alırız # print(futbolcu1.ad) # Bu satır hata verir |
Python’da İlerin Düzey Sınıf Kavramları
Nesne yönelimli programlama Python’da dört ana kavrama ayrılır. Her biri ayrı yazılarda detaylarıyla incelenmiştir. Bakınız:
- Inheritance (Miras): Bir sınıfın, başka bir sınıftan özelliklerini (nitelikler ve metotlar) miras almasına denir.
- Encapsulation (Kapsülleme): Veriyi ve işlevselliği bir araya getirerek erişimi kısıtlar.
- Polymorphism (Çok biçimlilik): Farklı sınıfların, aynı arayüze sahip olmasına rağmen farklı işlevselliğe sahip olması durumunu ifade eder. Yani aynı metot adı, farklı sınıflarda farklı işler yapabilir.
- Abstraction (Soyutlama): Kompleks bir sistemi, temel bileşenlerine ayırır. Soyut sınıflar ve soyut metotlar ile gerçeklenir.