Python'daki Classları Anlayamıyorum

Python’u ilk programlama dilim olarak öğrenmeyi arzuluyorum, ve Fırat Özgül beyin makeleleri sayesinde gerçekten de iyi bir yere geldim sayılır. Lakin konular ilerledikçe anlayamamaya, mantığından kopmaya başladığımı fark ettim. Belki biraz aptalımdır bilmiyorum lakin oldukça anlaşılır bir dille anlatılmasına rağmen alttaki sayfayı anlayamıyorum.
https://belgeler.yazbel.com/python-istihza/nesne_tabanli_programlama1.html#ornek-nitelikleri

Örneğin bir uygulama yazarken şu şekilde yazmak biraz ilkelce bir yöntem.

s1=input(“Acaba sayınızı tek mi arzu edersiniz yoksa çift mi?”)
try:
s2=float(input(Sayınızı kaça kadar istersiniz?))
except ValueError:
print(“Sadece sayı girebilirsiniz”)
sayılar=[sayı for sayı in range(s2)]
if lower(s1)==“tek”:
for i in sayılar:
if i%2==0:
print(i)
if lower(s1)=“çift”:
for i in sayılar:
if i%2==0:
print(i-1)
print(s2-1)

Tamam iyi kötü hatasıyla falan bir program uydurdum, lakin bunu profesyonelce yazmış olmak için (?) Class ile yazmam gerekiyordu. Yazıyı okuduktan sonra bu kodları Classa nasıl çevirebilirim hiç bilmiyorum. Mesela ki init ile sınıf örnekleri arasındaki fark ne, fikrim yok. Şımarıkça görünmek istemem lakin herhangi bir kişi yazdığım kodlar üzerinden biraz örnek verirseniz mutlu olurum.

Ayrıca:
-Classlarla yazsak ne yazmasak ne?
-def init(self, hebele) şeklinde tanımladığımız fonksiyonun ikinci parametresi ne anlama gelir?
-Konu dışı sayabilirsiniz lakin pratik için bir kaç şey önerebilir misiniz?

İlginiz için teşekkür ederim.

Merhaba, yazdığınız kodlarda ufak değişiklikler yapmak zorunda kaldım. Bu kodları prosedürel yöntemle yazalım önce:

while True:
    soru1 = input("Acaba sayınızı tek mi arzu edersiniz yoksa çift mi?\nCevap1:")
    if soru1.lower() == "tek":
        while True:
            s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
            if not s2.isnumeric():
                print("Sayı girmediniz.")
                continue
            elif s2.isnumeric():
                for i in range(int(s2)):
                    if i % 2 != 0:
                        print(i)
                break
        break
    elif soru1.lower() == "çift":
        while True:
            s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
            if not s2.isnumeric():
                print("Sayı girmediniz.")
                continue
            elif s2.isnumeric():
                for i in range(int(s2)):
                    if i % 2 == 0:
                        print(i)
                break
        break
    else:
        print("Lütfen cevap olarak '{}' veya '{}' yazınız.".format("tek", "çift"))
        continue

Yukarıdaki kodları dilerseniz bir sınıf içinde veya bir fonksiyon içinde de tanımlayabilirsiniz. Hangi durumlarda fonksiyon veya sınıf içinde tanımlanır, bunun belli bir kuralı yok aslında. Fakat şöyle bir senaryo düşünün: Diyelim yukarıda yazılmış kodların yaptığı işlemlere benzer işlemler yapan başka kodlarınız var. Bu kodları düzenli durması için bir sınıfın içine yerleştirebilirsiniz. Ayrıca daha sonra başka bir programda, bu sınıfı programınızın içine aktararak sınıfa veya örneğe ait fonksiyonu çağırıp istediğiniz işlemleri yapabilirsiniz.

Şimdi yukarıdaki kodları önce bir sınıfın sınıf metodu haline getirelim:

class Sinif1:
    @classmethod
    def sinif_metodu(cls):
        while True:
            soru1 = input("Acaba sayınızı tek mi arzu edersiniz yoksa çift mi?\nCevap1:")
            if soru1.lower() == "tek":
                while True:
                    s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
                    if not s2.isnumeric():
                        print("Sayı girmediniz.")
                        continue
                    elif s2.isnumeric():
                        for i in range(int(s2)):
                            if i % 2 != 0:
                                print(i)
                        break
                break
            elif soru1.lower() == "çift":
                while True:
                    s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
                    if not s2.isnumeric():
                        print("Sayı girmediniz.")
                        continue
                    elif s2.isnumeric():
                        for i in range(int(s2)):
                            if i % 2 == 0:
                                print(i)
                        break
                break
            else:
                print("Lütfen cevap olarak '{}' veya '{}' yazınız.".format("tek", "çift"))
                continue

Sınıf haline getirilmiş kodları çalıştırmak için bu kez şöyle bir çağırma yöntemi kullanırız:

Sinif1.sinif_metodu()

Şimdi ise bu kodları bir sınıfın örneğinin çağırdığı bir fonksiyon haline getirelim (örneğin çağırdığı fonksiyon örnek fonksiyonu değildir):

class Sinif2:
    def __init__(self):
        self.ornek_metodu()

    @staticmethod
    def ornek_metodu():
        while True:
            soru1 = input("Acaba sayınızı tek mi arzu edersiniz yoksa çift mi?\nCevap1:")
            if soru1.lower() == "tek":
                while True:
                    s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
                    if not s2.isnumeric():
                        print("Sayı girmediniz.")
                        continue
                    elif s2.isnumeric():
                        for i in range(int(s2)):
                            if i % 2 != 0:
                                print(i)
                        break
                break
            elif soru1.lower() == "çift":
                while True:
                    s2 = input("Sayınızı kaça kadar istersiniz?\nCevap2:")
                    if not s2.isnumeric():
                        print("Sayı girmediniz.")
                        continue
                    elif s2.isnumeric():
                        for i in range(int(s2)):
                            if i % 2 == 0:
                                print(i)
                        break
                break
            else:
                print("Lütfen cevap olarak '{}' veya '{}' yazınız.".format("tek", "çift"))
                continue

Son sınıfta yer alan fonksiyonun çağrılma şekli ilk sınıfa göre biraz daha farklıdır:

Sinif2()

Bu iki sınıf arasındaki fark; ilk sınıfın herhangi bir örneği veya örnek metodu yoktur. Sınıf ismi kapsayıcı bir ad gibi davranır. Bu sınıfın içine istediğiniz kadar fonksiyon ekleyip daha sonra hangi fonksiyonu kullanmak istiyorsanız onu çağırabilirsiniz. İkinci sınıf init fonksiyonuyla sınıfa ait bir örnek tanımlar. Bu sınıfın örneğinin init fonksiyonu içinde başka bir fonksiyonu çağırdığına dikkat edin, Şayet fonksiyonun içinde örnekle (yani selfle) bir işlem yapmak istemiş olsaydık, @staticmethod bezeyicisini kullanmamamız gerekirdi. Fakat self ile bir işlem yapmayacağımız için fonksiyona @staticmethod bezeyicisini ekledik. İlk sınıftaki @classmethod bezeyicisi ise fonksiyonun sınıfa ait olduğunu belirtir.

Sorularınıza gelecek olursak:

  1. Yazdığınız kodları bir modül olarak başka programlarda kullanmak istediğinizde, sınıfın içindeki bir metoda erişerek, metodu kendi programınızda kullanabilirsiniz. Mesela str() bir sınıf örneğini ifade eder ve str().lower() da bu örneğin kullandığı bir fonksiyonu. str().lower() fonksiyonunda olduğu gibi; önce sınıfa ait örneği sonra örnek metodunu çağırdığımız gibi kendi yazdığımız sınıflardaki fonksiyonları çağırabiliriz. Ama bazı sınıflardaki fonksiyonlara erişim örnek üzerinden değil de sınıf üzerinden yapılır. Örneğin modüller böyle sınıflardır. Bir fonksiyonu çağırdığımız gibi bir modülü çağıramayız. Yukarıdaki ilk sınıf örneği de bir modül gibi davranır ve bu sınıf da bir fonksiyon gibi çağrılamaz. Halbuki ikinci sınıfı bir fonksiyon gibi çağırabiliriz.

  2. init fonksiyonun ikinci parametresi herhangi bir argümandır, yani fonksiyonunuzda eğer bir argüman kullanmanız gerekiyorsa argümanları self ifadesinden sonra yazmak gerekir. Argüman sayısı ihtiyacınıza göre çoğaltılabilir.

  3. class ile yapılmış uygulama önerilerinden mi bahsediyorsunuz yoksa class konusunu anlatan önerilerden mi bahsediyorsunuz?

3 Beğeni

Aşağıdaki sınıf örneğine bir bakalım:

class Sinif3:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def ekrana_bastir(self):
        print("{}\n{}".format(self.x, self.y))


deneme1 = Sinif3(x=10, y=10)
# deneme1.x = 10
deneme2 = Sinif3(x="Merhaba", y="Dünya")
# deneme1.x = "Merhaba"

Yukarıdaki sinifin örneğinin nasıl özelliklere sahip olduğunu siz belirliyorsunuz. Sınıf içinde bir örnek metodu tanımlanmış olduğunu görüyorsunuz. Bu örnek metoduna ulaşmak için aşağıdaki işlemi yapmamız gerekir:

# Önce sınıfa ait bir örnek oluşturulur.
deneme3 = Sinif3(x=[1, 2, 3, 4], y="Merhaba Dünya")
# Sonra örnek fonksiyonu çağrılır.
deneme3.ekrana_bastir()

# Yukarıdaki işlemi yapabilmek için değişken tanımlamak yerine kodları aşağıdaki gibi de yazabilirdik:
Sinif3(x=[1, 2, 3, 4], y="Merhaba Dünya").ekrana_bastir()

Sinif3’teki ekrana_bastir() fonksiyonununda self ifadesi olmasının sebebi, fonksiyonun self.x ve self.y gibi örnek nitelikleriyle işlem yapmasıdır.

O halde, sizin verdiğiniz kodların 3. Sınıfı olacak şekilde, kodlarda değişikliğe giderek, bir sınıf oluşturulabilir ve bu sınıfın örnek metodu aşağıdaki gibi olabilir:

class Sinif3:
    def __init__(self, *args):
        """Bu kısımda, Sinif3() şeklinde örnek oluşturulurken,
        örneğe ait olacak özellikleri belirlersiniz ve bu
        özellikler, kullanıcıların girmiş olduğu bilgilerden veya bizzat
        burada tanıtacağınız bilgilerden elde edilir. Bu bilgilerden 
        birisi şu an okuduğunuz döküman da olabilir.
        Dökümana; Sinif3.__init__.__doc__  ile sınıf üzerinden, 
        Sinif3("tek", 10).__init__.__doc__ ile de örnek üzerinden 
        ulaşabilirsiniz.
        Argümanınız, kullanıcının girmek zorunda olduğu herhangi bir
        veri tipi olabilir. Verilerin sizin belirlediğiniz şartlara uygun
        yapıda olup olmadıkları önemlidir.
        Veri uyuşmazlığının olduğu durumlarda, kullanıcının sorunu
        çözmesine yardımcı olacak hata bildirimleri başka bir fonksiyon
        olarak tanımlanıp, bu blokun altında çağrılırsa, örnek oluşturulur
        oluşturulmaz devreye girer.
        """
        self.arg0 = args[0]
        self.arg1 = args[1]

    def ornek_metodu(self):

        if self.arg0 == "tek" and str(self.arg1).isnumeric():
            for i in range(int(self.arg1)):
                if i % 2 != 0:
                    print(i)

        elif self.arg0 == "çift" and str(self.arg1).isnumeric():
            for i in range(int(self.arg1)):
                if i % 2 == 0:
                    print(i)
                    
        # Başka elif durumları da olabilir...
        
        else:
            print("arg0: 'tek' veya 'çift' olmalıdır.\n"
                  "arg1: sayi olmalıdır.")
            
        
Sinif3("tek", 10).ornek_metodu()

Örnek metodunun çağrılma şeklinin sınıf().fonksiyon() şeklinde olması dikkatinizi çekmiş olmalı.

Umarım yardımcı olabilmişimdir, anlamadığınız yerlerle alakalı lütfen sorular sorun, ilgili arkadaşlar veya ben bilgimiz dahilinde cevaplamaya çalışırız.

3 Beğeni

Gerçekten öyle anlatmışsınız ki hani anlamasam dahi anlamadım demeye utanırım, ilginiz için teşekkür ederim ve bu konu hakkında zihnimi aydınlattığınızı rahatlıkla çekinmeden söyleyebilirim. Lakin @staticmethod ve @classmethod nedir bir fikrim yok. İnternette araştırsam da pek bir fikir alamadım. Açıklayabilirseniz umarım sizi yormuş olmam. Bunun yanı sıra, pratik yapmaktan kastım Python hakkında bildiklerimi somuta dökebileceğim ufak projeler ile uğraşmak. Şu şekilde sadece okuyor ve hiçbir şey üretmiyorum, bu durum benim pek hoşuma gitmiyor.

Son olarak unutmadan: “Instance” anlamında kullanılan örnek, tam olarak ne anlama geliyor anlayabilmiş değilim. Instance da İngilizce’de Example anlamına kullanılan bir kelime, bu kısmın kafamı karıştırdığını söyleyebilirim.

1 Beğeni

Örnek üzerinden anlatmaya çalışayım:

# Mesela sınıf örneğinin (instance'ın) sahip olduğu özelliklerden
# birisi zamanla alakalı olsun.
# Aşağıdaki modülü de zamanla alakalı bir işlemi yapabilmek
# amacıyla programın içine aktarmış olalım.
from datetime import datetime


class sinif_1:
    def __init__(self, isim="isim", tarih=datetime.now()):
        # self.isim'in veri tipinin str olduğunu görüyorsunuz.
        self.isim = isim
        # self.tarih verisi ise datetime ile oluşturulmuş.
        self.tarih = tarih
        # Kullanıcıdan istediğiniz veriler gördüğünüz gibi çok farklı tipte olabilir.

    @staticmethod
    def statik_fonksiyon():
        # statik_fonksiyon(), print() fonksiyonunu çağırıyor.
        # Ama gördüğünüz gibi statik_fonksiyonda instance ile
        # ilgili herhangi bir işlem yok.
        print("Merhaba Dünya")

    def ornek_fonksiyonu(self):
        # ornek_fonksiyonu ise self.isim ve self.tarih isimli
        # örnek niteliklerini kullanıyor. Fonksiyonun başındaki
        # self ifadesi bu sebeple ilk argüman olarak yazılır.
        print(self.isim, self.tarih)

# Diyelim instance'a ait tarih verisine ulaşmak istiyoruz.
# O zaman tarih verisine örneği tanımlayarak ulaşırız.

sinif_1().tarih

# Diyelim instance'ın fonksiyonunu kullanmak istedik.

sinif_1().ornek_fonksiyonu()

# Bu sefer statik fonksiyonu kullanmak istedik.

sinif_1().statik_fonksiyon()
# veya
sinif_1.statik_fonksiyon()

# Gördüğünüz gibi statik fonksiyona hem örnek hem de sınıf
# üzerinden ulaşabiliyoruz. Ama örnek fonksiyonuna sınıf
# üzerinden ulaşamayız. Yani;

sinif_1.ornek_fonksiyonu()

# kullanımı hatalı kullanımdır. TypeError hatası verir.

@classmethod’u örnek üzerinden anlatmaya çalışayım:

class sinif_2:
    # Sınıfımızın name isimli bir özelliği olsun
    name = "name"

    # classmethodu sınıfa ait bir özellik ile işlem yaptığımızda kullanırız.
    @classmethod
    def ekrana_bastir(cls):
        print(cls.name)

    # veya fonksiyonumuz, sınıf içinden bir özelliği kullanmıyorsa da 
    # bu bezeyiciyi kullanabiliriz.
    # Hatta böyle durumlarda, @classmethod yerine 
    # @staticmethod'u da kullanabilirsiniz.
    # ancak o zaman 'cls' ifadesini yazmamanız gerekir.
    @classmethod
    def baska_bir_fonksiyon(cls):
        print("Merhaba")

# sinif_2'nin name özelliğine şu şekilde erişebiliriz.

sinif_2.name

# veya ekrana_bastir() fonksiyonuna erişmek istiyoruz:

sinif_2.ekrana_bastir()

# veya baska_bir_fonksiyon isimli fonksiyona erişmek istiyoruz:

sinif_2.baska_bir_fonksiyon()

Örneğin yani instance’ın gösterimi sinif() şeklindedir, sınıfın gösterimi sinif şeklindedir.
Örnek fonksiyonuna şu şekilde ulaşırız: sinif().fonksiyon()
Sınıf fonksiyonuna şu şekilde ulaşırız: sinif.fonksiyon()

Pratik yapmakla alakalı neler yapabilirsiniz bir düşüneyim: Önce veri maniplasyonu ile ilgili alıştırmalar yapabilirsiniz. Verilere ne kadar hakim olursak bizim için o kadar iyi. Çünkü diyelim yarın öbür gün belki bir csv dosyası okuyacaksınız, bu dosyanın içindeki verileri çekip çıkartabilmek için veri maniplasyonu
yapmak zorunda kalabilirsiniz. Veya daha basit bir örnek üzerinden gidelim: “Merhaba” kelimesi içinde
geçen a harflerinin yerine nasıl “-” işaretini koyarsınız, bunlarla alakalı çalışmalar yapabilirsiniz. Veya elinizde iç içe geçmiş listeler var, bu listeleri nasıl ayıklarsınız, bunlarla alakalı çalışmalar yapabilirsiniz. Veya bir metin içinde tekrar eden kelimeleri ve sayısını hesaplayabilirsiniz.
Veya bir çember denklemi yazıp, çemberin köklerini bulan bir fonksiyon yazabilirsiniz. Veri tiplerini kontrol edebildiğinizi, verileri birbirlerine dönüştürmekte sıkıntı yaşamadığınızı gözlemlediğiniz zaman zaten daha ileri seviye çalışmalar yapmaya hazır olduğunuzu anlarsınız.

1 Beğeni

İlginiz için minnettarım, çok teşekkür ederim. Eğer kurallar gerektiriyorsa moderatör arkadaşlar konuyu kilitleyebilirler.

Python’daki nesne kavramı ile sözlük kavramı birbirine benzeyen kavramlardır. Python nesnelerinin nitelikleri ve metodları varken, Python’daki sözlüklerin anahtarları ve bu anahtarların değerleri vardır. Python nesnelerinin nitelik ve metodlarının isimlerini bir sözlüğün anahtarlarıymış gibi, nitelik ve metodların değerlerini de bu sözlüğün değerleri gibi düşünebilirsiniz.

Sözlük yapısının temelinde C dilindeki struct’lar yer alıyor aslında. C, object oriented bir dil olarak geçmez ama nesne tabanlı dillerde yer alan “nesne” kavramını, C’deki struct yapısını kullanarak oluşturabiliriz. Yani struct veri yapısı, yeni sınıflar oluşturmak için gerekli özellikleri sağlar. Benzer şekilde dict veri tipi de bir sınıf ve o sınıfa ait nitelikleri ve metodları oluşturmak için yeterlidir. Python’da struct’a en çok benzeyen veri tipi dict veri tipidir dersek herhalde abartmış olmayız.

Şimdi gelin, bir Python sözlüğü tanımlayalım:

sozluk = {
    "x": {
        "x_x1": {
            "x_x1_x1": 1,
            "x_x1_x2": lambda x: print(x)
        },
        "x_x2": lambda x: print(x)
    },
    "y": 2
}

Bu sözlük, bir sınıfın niteliklerinin ve metodlarının nasıl bir düzende yer aldığını gösteren bir eşleştirme olarak da kabul edilebilir aslında. Şimdi bu sözlüğü nesneye çeviren bir fonksiyon yazalım ve bu fonksiyon da aşağıdaki işlemleri yapsın;

Sözlüğün anahtarlarının değerlerinin tipleri eğer dict ise, o anahtarlar nesnelere işaret etsin. Aksi durumda, yani sözlüğün anahtarlarının değerlerinin tipleri dict dışında başka bir veri tipiyse, o anahtarlar da nesnenin niteliği veya metodu haline gelsin. Sonuç olarak sözlük içinde sözlük, nesne içinde nesne olarak dönüştürülsün.

Fonksiyonu yazalım:

def from_dict_to_obj(d: dict) -> object:
    obj = type("", (), {})
    for key, value in d.items():
        if isinstance(value, dict):
            setattr(obj, key, from_dict_to_obj(value))
        else:
            setattr(obj, key, value)
    return obj

Şimdi from_dict_to_obj isimli fonksiyonu, sozluk isimli sözlüğü argüman olarak yazarak çağıralım. Sonra da sözlükte anahtar olarak yer alan x_x1_x2 ismini, bir metod ismi olarak kullanalım.

obj = from_dict_to_obj(sozluk)
obj.x.x_x1.x_x1_x2("hello")

Çıktı:

hello

dict tipinden object’e dönüştürme yapabiliyoruz, peki object’ten dict’e bir dönüşüm yapacak olsaydık, nasıl yapardık?

def from_obj_to_dict(o: object) -> dict:
    dictionary = {}
    for key, value in {k: getattr(o, k) for k in dir(o) if not k.startswith("_")}.items():
        if isinstance(value, type):
            dictionary[key] = from_obj_to_dict(value)
        else:
            dictionary[key] = value
    return dictionary

Hemen bir örnek yapalım:

obj = from_dict_to_obj(sozluk)
print(from_obj_to_dict(obj))

Çıktı:

{'x': {'x_x1': {'x_x1_x1': 1, 'x_x1_x2': <function <lambda> at 0x000001B81B6A9760>}, 'x_x2': <function <lambda> at 0x000001B81B6A9C60>}, 'y': 2}

Başka bir örnek:

class Test:
    def __init__(self, x):
        self.x = x

    class It:
        y = 1

        def print(self, a):
            print(a)


test = Test(x=10)
print(from_obj_to_dict(test))

Çıktı:

{'It': {'print': <function Test.It.print at 0x000001F82DD65EE0>, 'y': 1}, 'x': 10}

Şöyle deneyelim:

test = Test(x=10)
_dict = from_obj_to_dict(test)
_dict["It"]["print"]("1", "hello")

Çıktı:

hello

Son paylaştığım kodlardaki son satırdaki ifade şöyle:

_dict["It"]["print"]("1", "hello")

Bu kısmı açıklayayım izninizle:

Test sınıfı içinde yer alan It sınıfında tanımlı print metoduna dikkat ederseniz, metodun ilk argümanının self olduğunu görürsünüz. Buradaki self, sınıftan oluşturacağımız bir nesneyi işaret eder. Dolayısıyla print metodu bir sınıf metodu değil, bir örnek metodudur.

İyi ama, biz yukardaki kodda It sınıfından bir örnek oluşturmadan, print fonksiyonunu kullanabildik? Bu nasıl oldu?

nesneyi, sözlüğe dönüştürdükten sonra, print metodunun ilk argümanı, herhangi bir sınıfın örneğini argüman olarak kabul eder, özellikle It sınıfından oluşturulan bir örneğe ihtiyaç duyulmaz.

Yani, bu kod da aynı çıktıyı üretir:

_dict["It"]["print"]([1, 2, 3], "hello")

Bu kod da:

_dict["It"]["print"](1, "hello")

Peki, bundan nasıl kurtuluruz?

class Test:
    def __init__(self, x):
        self.x = x

    class It:
        y = 1

        def print(a):
            print(a)

print fonksiyonunu, yukardaki gibi bir örnek veya sınıf metodu olmaktan çıkarırsak, artık _dict["It"]["print"](1, "hello") ifadesi yerine, _dict["It"]["print"]("hello") ifadesini kullanabiliriz.

Bu arada, metod, bir sınıf metodu olsaydı, yani, print fonksiyonu şöyle olsaydı:

class Test:
    def __init__(self, x):
        self.x = x

    class It:
        y = 1

        def print(cls, a):
            print(a)

Bu sefer, print fonksiyonunu çağırırken, ilk argüman olarak herhangi bir sınıfın kendisini argüman olarak verebilirdik:

_dict["It"]["print"](int, "hello")

Bu mesajda Python’daki nesneleri dinamik bir yolla oluşturmamızı sağlayan bir yaklaşımdan bahsetmeye çalıştım. Şimdilik anlatacaklarım bu kadar. Sorusu olan arkadaşların, sorularını yanıtlamaktan memnuniyet duyarım. Ayrıca katkı sağlamak isteyen arkadaşlara da şimdiden teşekkür ederim.

Konuyla bağlantılı olarak aşağıdaki başlığın da belki faydası olabilir.

Herkese iyi çalışmalar.

Aslında eksi bir konu olmasına rağmen günün en güzel sorusu.

Aslında çok katkı sağlamak istiyorum konuya.

C konusundaki kısma çokca yazmak isterim, nesneler konusunda fikirlerimi yazmak isterim. Nesnenin dinamik oluşturulması ne ki, runtime da kodu nasıl değiştireceğinizi bile yazmak isterim. İsterim de, asıl üzerinde durmak istediğim konu.

NYP ve classlar.

Sınıflar konusunu fayda/zarar durumunu konuşmak. Neden nasıl ihtiyaç duyacaklarını veya duymayacaklarını tercih ederdim.

Ortada bir teknoloji var. Nesneye yönelik programlama. Acaba bunu en iyi yapan dillerden biri python mı? Acaba herkes sınıfları kullanarak program yazmak zorunda mı? Bunu iyi bir şekilde ortaya koymak gerekir.

Kodları kenara koyalım.

Önce bu basit yalın soru hakkındaki fikirlerinizi paylaşırsanız, yani katılımcının classlarla yazsak ne yazmasak ne sorusu konusunda. Sonrasında ben de katkı sunmak isterim.

Kısa cevap, amaca uygun kodu class kullanmadan yazabiliyorsanız, kullanmayabilirsiniz elbette. Şu ana kadar hangi durumlarda class kullanma ihtiyacım oldu bir düşüneyim…

  1. Özellikleri bakımından diğer veri gruplarından farklı olması istenen bir veri tipine ve sadece bu veri tipine göre işlem yapan metodlara ihtiyaç duyduğum zamanlarda class kullanıyorum.

  2. Bir sınıfı taklit eden bir sınıfa ihtiyaç duyduğumda class kullanıyorum. Neden, belirli bir sınıfı taklit eden bir sınıfa ihtiyaç duyuyorum? Örneğin, str()'nin IO temelli bir sınıfın özelliklerini taklit etmesini istiyorum. Neden böyle bir şey yapmak istiyorum peki? Belki çıktıları sys.stdout’a değil de bir str nesnesine veya bir widgete aktarmam gerekiyordur. Bunun için de, sys.stdout metodlarına sahip, özelleştirilmiş bir sınıf tanımlıyorum.

  3. Bazen, fonksiyondan fonksiyona argüman taşıyıp durmamak, veya global değişkenler kullanmamak için sınıf kullanıyorum.

  4. C’de yazdığımız bir struct’ü Python’da kullanmak istediğimizde, Python’da da ctypes.Structure’u miras olarak alan bir tane class tanımlamak gerekir. Yani, başka bir dile ait bir veri tipini Python’da kullanabilmek için.

Bunlar dışında bir de sadece keyfi olarak class kullanmak istediğim zamanlarda class kullanıyorum. Yani bir çok durumda class kullanmayabilirsiniz. global değişkenler ve fonksiyonlar çoğu kez bir class kullanarak yapabileceğimiz şeyleri yapabilir. Yukarda bahsettiğim ilk neden benim açımdan oldukça geçerli bir neden; yani sınıf, verinin üretimi, verinin saklanması ve veri üzerinde işlem yapılması açısından işlevsellik sağlıyor, ayrıca benzer parçaların bir arada tutulması kodun okunurluğunu artırıyor.

Şimdilik söyleceklerim bu kadar. Katkılarınızı merakla bekliyorum.

2 Beğeni

Bu konunun mailimde bildirimlerini görünce pek hoşuma gitti. Şimdi her şeye net ve berrak bir gözden bakınca “Ne boşuna ezmişim” kendimi diye kızdım. :slight_smile:

Bu oop konusunun anısı biraz tuhaf bende. Programlamaya 18 yaşında falan başlayabilecekken, o da hobi olarak, 23 yaşımda başladım. Çünkü OOP dünyanın en önemli konusuydu ve bunu anlamayan da kendisine yazılımcı dememeli diye düşünüyordum. Bu arada, benim lisansım psikoloji. Psikolojide böyle kesin yargıların insanı nasıl mutsuz ettiğini ve psikologların ilk işinin bu kesin ön kabulleri temizlemek olduğunu öğrettiler. Anlamasam da C kodlayıp geçermişim.

Şimdi geriye bakınca sorunumu anlıyorum. İstihza, çok da yanlış olmayan bir bakış açısıyla öğretirken önce her şeyin teknik bir görevi ve somut amaçlar olduğunu söylüyor. Doğrusu yanlış değil, somut faydalar düzleminde programlama gerçekten anlaşılır lakin OOP konusu somut değil, gayet soyut. OOP öğrenirken programcı gözüyle bakmaktan çıkıp bir “kod tasarımcısı” gözüyle bakmakta fayda var.

OOP konusunu eğitimci kaynaklar ister istemez, onları suçlayamam, gerçek hayattan örnekler vererek oop konusunu anlaşılır kılmaya çalışıyor. İşte köpek, hayvan sınıfını miras alır gibi. Ama doğrusunu isterseniz, o ana kadar tamamen teknik ve “bilgisayar işi yapıyoruz biz koçum” diye çalışan akıl bir anda “Kedi mi? Köpek mi? Robot kedi mi yapıyoruz burada? Ne alaka? Ne oluyor yahu?” afallıyor. Ben bir kitap yazarsam eğer, programlamada kullanılan örnekler üzerinden anlatmayı tercih ederdim. Çünkü bilgisayar işi yapıyorsak bilgisayardan devam edelim, öbür türlüsü beni epey afallatmıştı.

Bir de… Bana kalırsa Python bu işin ruhunu yanlış aktarıyor bence. Aslında Python yanlış bir şey yapmıyor, fakat Ruby öğrenmeye başlayınca Ruby’nin yaklaşımı bana OOP anlayışını daha dille ilk karşılaştığım andan itibaren göstermişti. Python, her şey nesne olsa da, OOP anlayışını ek bir özellikmiş gibi sunuyor. O an lazım gelmiş de eklenmiş gibi. Nesnelerden operatörvari fonksiyonlarla bilgi almak bana hiç OOP mantığına uygun gelmemişti. Şimdi, programlamada belli bir seviyeye gelince ve OOP anlayışını çözünce Python’un tam bir OOP dili olduğunu kavradım, fakat öğrenirken insan gerçekten zorlanıyor.

Tabii zaman içerisinde OOP anlayışına karşı daha marjinal tavırlar almaya başladım. “Class” kelimesinin aslında Alan Kay’ın oop anlayışının çok dışında kaldığını, aslında odak noktasının nesneler değil de “mesajlar” olduğuna inanıyorum. Aktörler ve süreçler üzerinden işleyen Erlang/Elixir’in öz hakiki OOP olduğuna inanıyorum mesela. :slight_smile: Onun dışında prototip üzerinden işleyen oop’un class üzerinden işleyen oop’tan daha kolay anlaşıldığını düşünüyorum. Ek olarak; inheritance/miras alma bence kullanılmaması gereken ve her şeyi karışıklaştıran bir teknik, onun yerine composition’u tercih ederim ve öneririm.

Bir de, yazıyı yazarken aklıma gelmeyen fakat şimdi gelen bir şeyi de eklemek istiyorum, soyutlama. Soyutlama fikrini benimsemeden OOP fikrini de anlayamayız. Her şeyin teknik detaylara dayanmadığını, kod dizaynının insanın anlayabileceği bir biçimde de düzenlenmesi gerektiğini, karmaşık ve detaylı şeylerin daha basit temsiller üzerinden özetlenmesi gerekir ve bu soyutlamadır. OOP, bir programlama tekniği değil, bir programlama yöntemi ve aynı zamanda bir soyutlama tekniğidir. Bunu anlamakla OOP başlıyor.

2 Beğeni

class'ın kullanılma mantığı dediğiniz gibi aslında soyutlama yapmaktır. Mesela şöyle örnek vereyim: su, doğada var olan bir şeye verdiğimiz bir isimdir. Ama aynı zamanda H2O da su ile işaret ettiğimiz şeye işaret eder. Ancak su, H2O'dur diyemeyiz. Su, H2O’yu kapsayan, kavrayan bir kavramdır. Ve H2O’yu kapsayabildiği için de, H2O’ya göre daha soyut bir yapıdadır. Su, var olan bir şeyin bütününü, H2O ise var olan bir şeyin parçalarını işaret eder.

Peki biz günlük hayatta su yerine H2O kavramını kullanamaz mıyız? Kullanabiliriz ama gündelik hayatta, H2O kavramı ile kastettiğimiz şeye işaret eden ve insanın herhangi bir zahmete girmeden kavrayabildiği, daha aşkın bir anlamı olan başka bir kavram var; su. “Su” dediğimizde, insanın aklına genelde H2O’dan fazlası gelir.

Dolayısıyla bir sınıf, ilkel tiplere daha da fazla soyutluk kazandırabileceğimiz bir araç aslında. Bir tipin soyutluk kazanması demek, örneğin H2O olarak ifade edilen bir şeyin artık su olarak ifade edilmesi ve bir bütün olarak kavranması demektir. Dolayısıyla, “suyun” kavram olarak önemi ve kullanılma sıklığı, insan hayatı açısından, “H2O”'nun kavram olarak öneminden ve kullanılma sıklığından daha fazladır.

Özetle, class, kavram oluşturmaya yarar diyebiliriz.

3 Beğeni

Zaten gündelik hayatta kullandığımız Su’yun tam karşılığı H2O olmuyor. İçinde mineraller var, eser elementler var.

Her şey gerçekten dilde olup bitiyor gibi.

Hatta bunu iddia eden ideolojiler var fenomenoloji, logosenterizm vs…(acaba bu aklıma nereden geldi)

Bu class konusunu Heidegger’in “var olan” yani “entity” dediği şey ile de paralel bir şekilde düşünebilir miyiz düşünmeye çalışıyorum.

Bir var olan olarak “kürsü” ne türden bir özelliğe sahiptir mesela? Daha önce hayatında hiç kürsü görmemiş, onun kullanım amaçları hakkında herhangi bir deneyimi olmamış bir insan için “kürsü” kavramı, kürsünün malzemesini oluşturan ilkel tipleri dışında ne ifade edebilir mesela? O kürsüye baktığımızda, kürsüyü kürsü yapan kavram bütünlüğünü mü görürüz yoksa bir kaç parça tahtanın bir araya gelmesinden oluşan bir var olan mı görürüz?

İşte kürsü, bir classtır. Sadece kürsü olarak tek başına değil, başka kavramlarla birlikte ancak olanaklı olan bir şeydir. Mesela, okul kavramı olmadan kürsü kavramını düşünemeyiz. Dolayısıyla nesneler aslında sadece fiziksel şeylere işaret etmezler, o şeylerle birlikte var olan soyut dünyaya da işaret ederler.

Bu örneği, dillere uyarlayabilir miyiz?

Yani benim dilimde herhangi bir nesnenin tarif edilebilmesi için harfi tarife ihtiyaç yoktur. Bir ismin belirtme hali, o ismi yeterince tarif eder. Türkçe’de -i son ekiyle bu tarifi yapabiliriz. Ama her bir “entity” ismini, harfi tarif ile tarif eden bir dile göre “harfi tarif” kullanmamak nesnelerin cinsiyetinin bilinememesine yol açıyor. İyi ama şimdi şöyle bir soru sorabilirsin. Nesnelerin cinsiyeti var mı?

Benim dünyamda yok, ama başkalarının dünyasında var. Dolayısıyla konuştuğum dil ile de aslında “şeylere” verdiğim anlam ve bu şeylerle kurulan dünyanın hikayesi farklı oluyor.

Programlama dilleri de böyledir:

Bir dilde, bir şeyin özelliklerinin ne olduğunu belirten kurallar, diğer dildeki kurallardan farklıdır. Her programlama dilinde bir for döngüsü vardır. Tıpkı her doğal dilin cümle yapısında özne, yüklem ve nesne’ye ihtiyaç duyulması gibi.

Ama bu özne-nesne-yüklemin sırası başka dillerde farklı olabilir. Dilin sadece bu farklılığı bile, bir hikaye anlatırken, kurgunun ilerleme şeklinin nasıl olabileceği hakkında fikir verir. Ya da bir dilin gramer özellikleri, şeyleri analiz etmeye mi yoksa şeylerle sentez yapmaya mı elverişli olduğunu belirler. Mesela Türkçe’de sık yapılan hatalardan birisinin nedeni özne ile yüklemin, araya giren nesnelerden ötürü birbirinden uzaklaşmasıyla ilgilidir. Bu uzaklaşma nedeniyle, bazen cümlelerdeki özne ve yüklem uyumunun bozulduğunu fark edebilirsiniz.

Aslında sınıf kavramının kendisi de bir class tır. Kürsü örneğine benzer şekilde.

Hatadan ziyade, Türkçe’de öğeleri (kök haliyle) istediğin şekilde sıralayıp anlamı yükleyebilirsin. Bu bir hatadan ziyade, sondan eklemeli dillerin gelişim biçimi. Aslında zorlanırsa bütün dillerde olabilir bu. Bence.

Her dilin sentezi yapılabilir, sadece iç işleyiş yasalarını çıkarmak mesele tabi. Bazılarını 10-20 mili saniyeler ile çerçevelerler, bazılarını daha düşük ya da daha yüksek… Ama gördüğümüz şu ki, bütün diller hecelerin ngramlar gibi evrilmesi ile kelimelerini(ya da konu gereği entitylerini) geliştirdi. Ki bu süreç az buz değil, FOXP2 geninin(yani insanların bugünküne benzer heceli konuşma sesi çıkarmasını sağlayan kasları kodlayan genin) geçimişi 800 000 yıl geriye gidiyor.

Bunu düşününce, evrimin dildeki bu çeşitliliğini sorgulamam.

Dillerin, yani dillerdeki isimlerin cinsiyeti, gerçekten ilgi çekici prehistorik bir konu ama, muhtemelen ünvanların evrimi ile ilgili olmalı(ataerkil-anaerkil çekişmelerin etkisi).

Bütün bunların yanında, açıkça belirtmem gerekir ki, “kürsü” kelimesini(en azından türkçe haliyle) bilmeyen biri için ne türden bir özelliğe sahiptir, sorunsalının deneyini yaşadık:

I speech kürsü.
I speech kürsü! - YouTube

Konu tam da tahmin ettiğim gibi çok da eğlenceli bir hale gelmiş.

Bir soru daha ortaya atıp daha sonra devam edeyim.

Sınıflar işleri kolaylaştırıyorsa neden öğrenmekte zorlanıyorlar?

Vinç de kürek de toprak çıkartmaya yarıyor. Vinç işleri kolaylaştırıyor ancak daha zor öğreniliyor :slight_smile:

" Vinç de kürek de toprak çıkartmaya yarıyor. Vinç işleri kolaylaştırıyor ancak daha zor öğreniliyor"

Tam da duyacağımı tahmin ettiğim bir cümleydi.

Bunun üstüne motorlu testereyle ekmek kesilmez eklersem belki vakit bulunca yazacaklarım hakkında biraz fikir verir.

“Vinç işleri kolaylaştırıyor ancak daha zor öğreniliyor”

bu önerme doğru değil.
Ben vinci daha kolay kullandım. Kürek zor. Belirli bir süre mavi yaka çalışmadan ehli olamazsın küreğin. El işi deyip geçmeyin, kaç milyon yıllık elin evrimi haberiniz var mı…(şaka şaka buraya girmeyeceğim, uzun sürer)

“Bunun üstüne motorlu testereyle ekmek kesilmez”

Bence el testeresi kullan, evrime saygınız olsun. El emeği göz nuru diye boşuna mı demiş atalarımız. Kaç milyon yıllık atalarımız…

Bence konuyu diğer programlama dillerinin çerçevesinde incelemek gerekiyor. Rust, Go gibi diller ilk bakışta OOP olmasa dahi OOP denecek pek özelliği içselleştirmişler ve kullanıma sormuşlar. OOP mantığı kurgulanabiliyor. C yazarken bile bir şekilde OOP mantığına dönüyorsunuz ki, kendi içinde ilkel de olsa inheritence destekleyen bir dil C.

Öğrenmesi zor ama tek bir cevap var, yazılımcıya bir yaklaşımı ve kod yazma tarzını benimsetiyor. Öğrenmesi tabii ki zor, yukarıda bahsettiğim gibi teknik bir detay olmaktan öte bir düşünüş şeklini benimsettiği için anlaması zor.