Bu makalemizde, Python’da Nesne Tabanlı Programlama OOP(Obejct-Oriented Programming) nedir ve nasıl yapılır makalemizin devamı niteliğinde anlatıyor olacağım.
6. Encapsulation(Kapsülleme):
Nesne tabanlı programlamada verilerin ve bunları manipüle eden işlevlerin birlikte gruplandırılmasını sağlar. Bu, verilerin dışarıdan doğrudan erişilmesini engeller ve erişimi kontrol altına alır.
Encapsulation, verilerin sınıf içindeki özel (private) bir durumda tutulmasını ve sadece sınıf içinde tanımlanan yöntemlerle erişilebilmesini sağlar. Bu, verilerin yanlışlıkla değiştirilmesini veya hatalı kullanılmasını önler ve veri bütünlüğünü korur. Sınıfın dışındaki kullanıcılar, sadece sınıf tarafından sağlanan arayüz üzerinden verilere erişebilir ve bu arayüz ile verilerin nasıl kullanılacağını bilebilirler.
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 31 32 33 |
class Kisi: def __init__(self,adi,soyadi,yasi): self._adi=adi self._soyadi=soyadi self._yasi=yasi def adiAl(self): return self._adi def soyadiAl(self): return self._soyadi def yasiAl(self): return self._yasi def yasiAyarla(self,yeni_yas): if yeni_yas>0: self._yasi=yeni_yas else: print("geçersiz yaş değeri") def adiAyarla(self,yeni_ad): if isinstance(yeni_ad,str): self._adi=yeni_ad else: print("geçersiz bir ad") kisi1=Kisi("ali","kaya","52") print(kisi1.adiAl()) #Burada Ali yazar print(kisi1.soyadiAl()) kisi1.adiAyarla("Mehmet") #metot üzerinden değişim yapıp artık ismi Mehmet yaptık print(kisi1.adiAl()) |
Yukarıdaki örnekte, “_adi“, “_soyadi” ve “_yasi” özellikleri özel (private) olarak tanımlanmıştır. Bunlar, başlarına tek bir alt çizgi koymak suretiyle diğer programcıların doğrudan erişimini engellemek için sınıf içinde kullanılan bir kuraldır. Bunun yerine, adiAl, yasiAl, yasiAyarla ve adiAyarla gibi metotlar, bu özelliklere erişmek için kullanılır.
Böylece, bir “Kisi” nesnesi oluşturduğunuzda, özel olan “_adi“, “_soyadi” ve “_yasi” özelliklerine doğrudan erişim sağlanamaz. Bunun yerine, “adiAl“,”soyadiAl” ve “yasiAl ” gibi metotlar kullanılarak bu verilere erişilebilir. “yasiAyarla ” metodu ise yaşın geçerli bir değere sahip olmasını ve “adiAyarla” metodu ise bir string ifade olmasını kontrol ederek güncellemeyi gerçekleştirir.
Yukarıdaki örnekte kullandığımız isinstance() fonksiyonundan bahsedecek olursak, bir nesnenin belirli bir veri türüne (class veya type) ait olup olmadığını kontrol etmek için kullanılır. Bu fonksiyon, verilen nesnenin belirtilen veri türünden türetilmiş bir örnek olup olmadığını kontrol eder.
Bizim örneğimizde ise isinstance(yeni_adi, str) ifadesi yeni_adi değişkeninin str (string) veri türüne ait bir örnek olup olmadığını kontrol eder. Eğer yeni_adi değişkeni bir string ise, self._adi değişkeni güncellenir. Eğer yeni_adi değişkeni başka bir veri türünden ise, örneğin bir sayı veya liste ise, o zaman Geçersiz ad değeri. mesajı yazdırılır.
Kodun ekran çıktısı:
Eğer kodu aşağıdaki gibi yazsaydık, yani adiAyarla() fonksiyonunu yazmasaydık:
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 |
class Kisi: def __init__(self,adi,soyadi,yasi): self._adi=adi self._soyadi=soyadi self._yasi=yasi def adiAl(self): return self._adi def soyadiAl(self): return self._soyadi def yasiAl(self): return self._yasi def yasiAyarla(self,yeni_yas): if yeni_yas>0: self._yasi=yeni_yas else: print("geçersiz yaş değeri") kisi1=Kisi("ali","kaya","52") print(kisi1.adiAl()) #Burada Ali yazar print(kisi1.soyadiAl()) kisi1._adi="mehmet" #Burada yine değişim yapar ama encapsulation’ı ihlal etmiş oluruz print(kisi1._adi) |
ve son iki satırda kisi1._adi=”mehmet” şeklinde direk private değişkeni üzerinde değişiklik yapasaydık Encapsulation kuralını ihlal etmiş oluruz bunun önüne geçmek için biz içerisine adiAyarla() fonksiyonumuzu yazdık kontrollerimizi ve değişimlerimizi burada yazdık.
1 2 3 4 5 |
def adiAyarla(self,yeni_ad): if isinstance(yeni_ad,str): self._adi=yeni_ad else: print("geçersiz bir ad") |
Sonuç olarak, Encapsulation, bir sınıfın içindeki verilere erişimin ve değişimin sınırlanması ilkesidir. Sınıfın dışından _adi gibi bir değişkene doğrudan erişim, encapsulation’ı ihlal ettiği için doğru bir uygulama değildir.
Python’da, _adi şeklinde tanımlanan değişkenlere doğrudan erişim yapılabilmesine rağmen, yine de encapsulation ilkesini korumak ve sınıfın dışındaki erişimi kısıtlamak önemlidir. Bunun için _adi gibi değişkenlere erişmek için get ve set metodları kullanılmalıdır, yani adiAl() ve yasiAyarla() gibi metodlar.
Eğer _adi değişkenine doğrudan erişim yaparsanız, encapsulation ilkesini ihlal etmiş olursunuz ve sınıfın içindeki kontrolleri atlamış olursunuz. Bu durumda, _adi değişkeninin değerini doğrudan değiştirebilirsiniz. Ancak bu, iyi bir programlama pratiği değildir ve sınıfın içindeki yöntemleri kullanmanız önerilir.
Sonuç olarak, Encapsulation sayesinde, verilerin doğrudan değiştirilmesi yerine sınıf tarafından kontrol edilen bir arayüz aracılığıyla erişilebilir ve manipüle edilebilir. Bu da veri güvenliğini ve istenmeyen hataları önler.
Encapsulation, verilerin korunması ve kontrolü sağlayarak programın daha güvenli, esnek ve sürdürülebilir olmasını sağlar.
7. Polymorphism (Çok Biçimlilik)
Polimorfizm, aynı isimle fakat farklı parametrelerle veya farklı davranışlarla çalışabilen nesnelerin kullanılmasını ifade eder. Yani, farklı sınıfların aynı isimli metotlarına erişerek farklı davranışlar sergileyebiliriz.
Şekilde de çizdiğimiz gibi üç tane sınıfımız olsun ve biri parent class(Kus sınıfı ) diğer ikisi child class olsun yani kendisinden miras alan classlar burada polimorfizmi kendi içlerinde bulunan uçma() metodu ile yapacağız sınıflar dışında tanımladığımız kusuUcur() metodu parametre olarak oluşturduğumuz devekusu1 ve kirlangic1 nesnelerini veririz böylece verdiğimiz nesnelere göre farklı sınıfların aynı isimli metotlarının, farklı davranmasını sağlamış olduk.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Kus: def ucma(self): print("kuş sınıfı metodu") class Kirlangic(Kus): def ucma(self): print("kırlangıçlar uçabilir") class DeveKusu(Kus): def ucma(self): print("Deve kuşları uçamaz") def kusuUcur(kus): kus.ucma() kirlangic1=Kirlangic() devekusu1=DeveKusu() kusuUcur(kirlangic1) kusuUcur(devekusu1) |
Polimorfizm, farklı sınıfların aynı isimli metotları farklı şekillerde uygulayabilmesidir. Kirlangic ve DeveKusu sınıfları, Kus sınıfını miras aldığından ve ucma fonksiyonunu ezdiklerinden dolayı, bu sınıfların kendi ucma metotları farklı bir şekilde çalışır.
kusuUcur fonksiyonu ise, Kus sınıfından türetilen herhangi bir nesneyi parametre olarak alır ve ucma metodunu çağırır. Bu sayede, kusuUcur(kirlangic1) ifadesinde kirlangic1 nesnesi Kirlangic sınıfına ait ucma metodu çalışır ve “kırlangıçlar uçabilir” mesajı yazdırılır. Benzer şekilde, kusuUcur(devekusu1) ifadesinde devekusu1 nesnesi DeveKusu sınıfına ait ucma metodu çalışır ve “Deve kuşları uçamaz” mesajı yazdırılır.
Kodumuzun ekran çıktısı aşağıdaki gibidir:
8. Abstraction (Soyutlama)
Abstraction, karmaşık bir sistemdeki detayları gizleyerek ve yalnızca ilgili olan özellikleri ve işlevleri vurgulayarak programlama yapılarını tasarlama ve kullanma yaklaşımıdır.
Abstraction, bir nesnenin önemli özelliklerini ve davranışlarını vurgulayarak kullanıcılara yalın bir arayüz sağlar. Bu şekilde, kullanıcılar nesnenin nasıl çalıştığını veya nasıl uygulandığını bilmek zorunda kalmadan, sadece nesneyle etkileşime geçerek istenen sonuçları elde edebilirler.
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 31 32 33 34 35 36 37 |
from abc import ABC, abstractmethod class Sekil(ABC): @abstractmethod def AlanBul(self): pass @abstractmethod def cevreBul(self): pass class Daire(Sekil): def __init__(self,yaricap): self.yaricap=yaricap def AlanBul(self): return 3.14*self.yaricap**2 def cevreBul(self): return 2*3.14*self.yaricap class Dikdortgen(Sekil): def __init__(self,kisaKenar,uzunKenar): self.kisaKenar=kisaKenar self.uzunKenar=uzunKenar def AlanBul(self): return self.kisaKenar*self.uzunKenar def cevreBul(self): return 2*(self.kisaKenar+self.uzunKenar) daire1=Daire(3) dikdortgen1=Dikdortgen(2,5) print(f"Yarıçapı {daire1.yaricap} Dairenin Alanı: {daire1.AlanBul()} ") print(f"Yarıçapı {daire1.yaricap} Dairenin Çevresi: {daire1.cevreBul()} ") print(f"kısa kenarı {dikdortgen1.kisaKenar} ve uzun kenarı {dikdortgen1.uzunKenar} Dikdörtgenin alanı: {dikdortgen1.AlanBul()} ") print(f"kısa kenarı {dikdortgen1.kisaKenar} ve uzun kenarı {dikdortgen1.uzunKenar} Dikdörtgenin çevresi: {dikdortgen1.cevreBul()}") |
Yukarıdaki örnekte, “Sekil” sınıfı soyut bir sınıftır. Soyut sınıflar, bir veya daha fazla soyut metodu içerir ve bu metotların alt sınıflar tarafından uygulanması gerekmektedir. “Sekil” sınıfında, “alanBul()” ve “cevreBul()” adında soyut metotlar tanımlanmıştır. Bu metotlar, alt sınıflar tarafından farklı şekillerde uygulanmalıdır.
Örneğimizde, biz ” Daire” ve ” Dikdortgen” adında iki alt sınıf oluşturduk. ” Daire ” ve “Dikdortgen” sınıfları “Sekil” soyut sınıfını miras alır. Her iki sınıf da ” alanBul()” ve “cevreBul() ” metotlarını uygular. Bu sayede, her şeklin alanını ve çevresini hesaplama yeteneğine sahip olurlar.
Bu örnekte, “ Sekil ” soyut sınıfı, tüm şekillerin ortak özelliklerini ve davranışlarını temsil eder. Alt sınıflar, soyut metotları uygulayarak kendi şekillerine özgü hesaplamaları gerçekleştirirler. Kullanıcılar, “ Sekil “ sınıfından türetilen nesneleri kullanırken, sadece “alanBul ()” ve “cevreBul()” ” gibi soyutlanmış metotları çağırarak şekillerin özelliklerini kullanabilirler.
Sonuç olarak, eğer burada “Sekil ” sınıfında “@abstractmethod” şeklinde tanımlanan metotlar varsa soyut sınıftan miras alan diğer sınıflara yazılmayı zorunlu kılar yoksa nesne üretildiğinde hata oluşur. Yani “Dikdortgen” ve “Daire “sınıfı, Sekil sınıfından kalıtım aldıkları için sekil sınıfında bulunan iki soyut metodu da “Dikdortgen” ve “Daire” sınıfında yazmak zorundayız.
Abstraction, karmaşıklığı azaltır ve programın daha anlaşılır ve yönetilebilir olmasını sağlar. Kullanıcılar, detaylara girmeden soyutlamayı kullanarak işlevselliğe odaklanabilirler.
Kodumuzun ekran çıktısı aşağıdaki gibidir:
0 Yorum