Ana içeriğe atla

Python Tanımlayıcılar (Descriptors) Eğitimi

Python Tanımlayıcıların ne olduğunu, ne zaman ve neden kullanmanız gerektiğini öğrenin.
Güncel 3 Eki 2026  · 13 dk. oku

Yapay Zeka ile Keşfedin

ChatGPTClaudePerplexity

Python tanımlayıcıları (descriptors) ya da daha genel adıyla tanımlayıcılar, sınıflar arasında paylaşılabilen, yeniden kullanılabilir kod yazmanız için güçlü bir teknik sunar. Kalıtıma benziyor gibi görünse de teknik olarak aynı şey değildir. Nitelik erişimini yakalamanın genel amaçlı bir yoludur. Tanımlayıcılar; property’lerin, static method’ların, class method’ların, super metodlarının vb. arkasındaki mekanizmadır.

Tanımlayıcılar Python 2.2 sürümünde eklendi ve o zamandan beri geleneksel sınıflara yeni bir tarz kazandıran sihirli yapılar olarak görüldü. Başka bir sınıfta yönetilen özellikler (managed properties) oluşturmanıza olanak tanıyan sınıflardır. Özellikle, birçok nedenle ilgi çekici hale gelen __get__(), __set__() ve __delete__() yöntemlerinin arayüzünü uygularlar. Örneğin, Python’da görmüş olabileceğiniz sınıf dekoratörleri ve property dekoratörleri gibi.

Basitçe söylemek gerekirse, bir nesne için __get__(), __set()__ veya __delete()__ yöntemlerini uygulayan bir sınıfa "Tanımlayıcı" denir. Python’un resmi belgelerinden doğrudan alıntıyla, bir tanımlayıcı, öznitelik erişimi tanımlayıcı protokolündeki yöntemlerle geçersiz kılınmış olan, bağlama davranışına sahip bir nesne özniteliğidir. Bu yöntemler __get__(), __set__() ve __delete__()’dir (Kaynak).

Tanımlayıcılar bağlamında bağlama davranışı, belirli bir değişken, nesne veya veri kümesi için değerin nasıl ayarlanabileceğinin, sorgulanabileceğinin (get) veya silinebileceğinin bağlanması anlamına gelir. Bu etkileşimin tamamı, yalnızca üzerinde ayarladığınız veriye uygulandığı için bu veri parçasına bağlanır ve sadece ilgili veriye özgü olur.

Tanımlayıcılar veri tanımlayıcıları ve veri olmayan tanımlayıcılar olarak sınıflandırılabilir. Yazdığınız tanımlayıcı yalnızca __get__() yöntemine sahipse buna veri olmayan (non-data) tanımlayıcı denir; buna karşılık __set__() ve __delete__() yöntemlerini içeren uygulama veri tanımlayıcısı (data descriptor) olarak adlandırılır. Veri olmayan tanımlayıcılar yalnızca okunabilirdir; veri tanımlayıcıları ise hem okunabilir hem yazılabilir.

Tanımlayıcıların bir sınıfa atandığını, bir sınıfın örneğine atanmadığını unutmamak önemlidir. Sınıfı değiştirmek, kodunu tetiklemek yerine tanımlayıcının kendisini geçersiz kılar veya siler (IBM Developer).

Son olarak, tanımlayıcı sınıf yalnızca bu üç yöntemle sınırlı değildir; yani get, set ve delete yöntemlerinin yanı sıra başka öznitelikler de içerebilir.

Haydi IBM Developer sayfasından ilhamla get, set ve delete yöntemlerini biraz daha ayrıntılı inceleyelim:

  • self, oluşturduğunuz tanımlayıcının örneğidir (Real Python).
  • object, tanımlayıcınızın eklendiği nesnenin örneğidir (Real Python).
  • type, tanımlayıcının eklendiği nesnenin türüdür (Real Python).
  • value, tanımlayıcının özniteliğine atanan değerdir. get(self, object, type) set(self, object, value) delete(self, object)
  • __get__(), bir özniteliğe eriştiğinizde ya da bazı bilgileri çıkarmak istediğinizde çalışır. Özniteliğin değerini döndürür; istenen öznitelik yoksa AttributeError istisnası yükseltir.

  • __set__(), bir öznitelik atama işlemi sırasında çağrılır ve özniteliğin değerini ayarlar. Bir şey döndürmez; ancak AttributeError istisnası yükseltebilir.

  • __delete__(), bir silme işlemini kontrol eder; yani bir özniteliği bir nesneden silmek istediğinizde çağrılır. Bir şey döndürmez.

Şimdi Tanımlayıcıların amacını birkaç örnekle anlayalım!

Tanımlayıcıların Amacı

car adında, make, model ve fuel_cap olmak üzere üç özniteliği olan bir sınıf tanımlayalım. Sınıfın özniteliklerini başlatmak için __init__() yöntemini kullanacaksınız. Ardından, nesneyi oluştururken sınıfa ilettiğiniz üç özniteliğin çıktısını döndüren sihirli __str__() fonksiyonunu kullanacaksınız.

__str__() yönteminin nesnenin dize gösterimini döndürdüğünü unutmayın. Bir sınıfın nesnesi üzerinde print() veya str() fonksiyonu çağrıldığında çalışır.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car1 = Car("BMW","X7",40)
print(car1)
BMW model X7 with a fuel capacity of 40 ltr.

Gördüğünüz gibi, çıktı gayet iyi görünüyor!

Şimdi arabanın yakıt kapasitesini eksi 40 olarak değiştirelim.

car2 = Car("BMW","X7",-40)
print(car2)
BMW model X7 with a fuel capacity of -40 ltr.

Bir dakika, burada bir terslik var, değil mi? Arabanın yakıt kapasitesi asla negatif olamaz. Ancak Python bunu hatasız bir girdi olarak kabul ediyor. Bunun nedeni, Python’un açık tip denetimini desteklemeyen dinamik bir programlama dili olmasıdır.

Bu sorunu önlemek için __init__() yöntemine bir if koşulu ekleyelim ve girilen yakıt kapasitesinin geçerli olup olmadığını kontrol edelim. Girilen yakıt kapasitesi geçersizse, bir ValueError istisnası yükseltelim.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car1 = Car("BMW","X7",40)
print(car1)
BMW model X7 with a fuel capacity of 40 ltr.
car2 = Car("BMW","X7",-40)
print(car2)
----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-22-1c3d23be72f7> in <module>
----> 1 car2 = Car("BMW","X7",-40)
      2 print(car2)


<ipython-input-20-1e154783588d> in __init__(self, make, model, fuel_cap)
      5         self.fuel_cap = fuel_cap
      6         if self.fuel_cap < 0:
----> 7             raise ValueError("Fuel Capacity can never be less than zero")
      8
      9     def __str__(self):


ValueError: Fuel Capacity can never be less than zero

Yukarıdaki çıktıda gördüğünüz gibi, yakıt kapasitesi sıfırın altında olduğunda program bir ValueError yükselttiği için şu an her şey yolunda.

Ancak başka bir sorun daha olabilir; örneğin girilen yakıt kapasitesi ondalık bir değer ya da bir string olabilir. Sadece yakıt kapasitesi değil, arabanın make ve model değerleri de bir tamsayı olabilir. Bu durumların hiçbirinde program bir istisna yükseltmeyecektir.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car(-40,"X7",40)
print(car2)
-40 model X7 with a fuel capacity of 40 ltr.

Yukarıdaki durumu da ele almak için bir başka if koşulu eklemeyi ya da tip denetimi için isinstance yönteminden yararlanmayı düşünebilirsiniz.

Bu kez hatayı ele almak için yerleşik isinstance yöntemini kullanalım.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if isinstance(self.make, str):
            print(self.make)
        else:
            raise ValueError("Make of the car can never be an integer")

        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
BMW
BMW model X7 with a fuel capacity of 40 ltr.
car2 = Car(-40,"X7",40)
print(car2)
----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-34-75b08cba454f> in <module>
----> 1 car2 = Car(-40,"X7",40)
      2 print(car2)


<ipython-input-31-175690bf3b98> in __init__(self, make, model, fuel_cap)
      7             print(self.make)
      8         else:
----> 9             raise ValueError("Make of the car can never be an integer")
     10
     11         if self.fuel_cap < 0:


ValueError: Make of the car can never be an integer

Harika! Böylece bu hatayı da ele alabildiniz.

Ancak ya daha sonra yakıt kapasitesi özniteliğini bilerek eksi 40’a değiştirmek isterseniz? Bu durumda çalışmayacaktır; çünkü tip denetimi yalnızca bir kez, __init__() yönteminde yapılır. Bildiğiniz gibi __init__() yöntemi bir kurucudur ve yalnızca sınıfın bir nesnesini oluşturduğunuzda bir kez çağrılır. Dolayısıyla özel tip denetimi daha sonra başarısız olacaktır.

Bunu bir örnekle anlayalım.

class Car:
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap
        if isinstance(self.make, str):
            print(self.make)
        else:
            raise ValueError("Make of the car can never be an integer")

        if self.fuel_cap < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
BMW
BMW model X7 with a fuel capacity of 40 ltr.
car2.make = -40
print(car2)
-40 model X7 with a fuel capacity of 40 ltr.

İşte böyle! Tip denetimini baypas edebildiniz.

Şimdi şöyle düşünün: Yakıt tüketimi, fiyat, aksesuarlar vb. tip denetimi gerektiren birçok başka özniteliğiniz de olabilir ve bu özniteliklerin bazılarının yalnızca okuma erişimine sahip olmasını da isteyebilirsiniz. Bu sinir bozucu olmaz mıydı?

İşte tüm bu sorunları ele almak için Python’un kurtarıcısı Tanımlayıcılar devreye girer!

Yukarıda öğrendiğiniz gibi, bir nesne için __get__(), __set()__ veya __delete()__ sihirli yöntemlerini uygulayan herhangi bir sınıfa Tanımlayıcı denir. Ayrıca bir özniteliğin nasıl davranması gerektiği üzerinde, örneğin okuma ya da yazma erişimine sahip olup olmayacağı gibi, ek kontrol sağlarlar.

Şimdi yukarıdaki örneği Python Tanımlayıcı yöntemlerini ekleyerek genişletelim.

class Descriptor:
    def __init__(self):
        self.__fuel_cap = 0
    def __get__(self, instance, owner):    
        return self.__fuel_cap
    def __set__(self, instance, value):
        if isinstance(value, int):
            print(value)
        else:
            raise TypeError("Fuel Capacity can only be an integer")

        if value < 0:
            raise ValueError("Fuel Capacity can never be less than zero")

        self.__fuel_cap = value

    def __delete__(self, instance):
        del self.__fuel_cap
class Car:
    fuel_cap = Descriptor()
    def __init__(self,make,model,fuel_cap):
        self.make = make
        self.model = model
        self.fuel_cap = fuel_cap

    def __str__(self):
        return "{0} model {1} with a fuel capacity of {2} ltr.".format(self.make,self.model,self.fuel_cap)
car2 = Car("BMW","X7",40)
print(car2)
40
BMW model X7 with a fuel capacity of 40 ltr.

Tanımlayıcı sınıfı ilk bakışta anlaşılmaz geliyorsa endişelenmeyin. Parçalara ayıralım ve her bir yöntemin ne yaptığını anlayalım.

  • Descriptor sınıfının __init__() yöntemi yerel değişken __fuel_cap’i sıfıra ayarlar. Başındaki dunder (çift alt çizgi) bu değişkenin özel (private) olduğunu belirtir. Başta dunder kullanmanın amacı, Descriptor sınıfının yakıt kapasitesi özniteliği ile Car sınıfındakini ayırt etmektir.
  • Artık bildiğiniz gibi __get__() yöntemi öznitelikleri almak için kullanılır ve yakıt kapasitesi değişkenini döndürür. Üç argüman alır: tanımlayıcı nesnenin kendisi, tanımlayıcı nesne örneğini içeren sınıfın örneği (yani car2) ve son olarak örneğin ait olduğu sınıf (yani Car sınıfı). Bu yöntemde, __set__() yönteminde ayarlanan fuel_cap değerini döndürürsünüz.
  • __set__() yöntemi, bir özniteliğe değer atandığında çağrılır ve __get__() yönteminden farklı olarak bir şey döndürmez. Tanımlayıcı nesnenin yanı sıra iki argümanı vardır: __get__() ile aynı olan örnek (instance) ve özniteliğe atadığınız value. Bu yöntemde, fuel_cap özniteliğine atamak istediğiniz değerin tamsayı olup olmadığını kontrol edersiniz. Değilse bir TypeError istisnası yükseltirsiniz. Ardından aynı yöntemde değerin sıfırdan küçük olup olmadığını kontrol eder, küçükse bu kez bir ValueError istisnası yükseltirsiniz. Hataları kontrol ettikten sonra fuel_cap özniteliğini bu değere güncellersiniz.
  • Son olarak __delete__() yöntemi, bir öznitelik bir nesneden silindiğinde çağrılır ve __set__() yöntemi gibi bir şey döndürmez.

Car sınıfı öncekiyle aynı kalır. Ancak yaptığınız tek değişiklik, Descriptor() sınıfının fuel_cap örneğini eklemektir. Daha önce belirtildiği gibi, tanımlayıcı sınıfının örneğinin bir örnek özniteliği olarak değil, sınıf özniteliği olarak eklenmesi gerektiğine dikkat edin.

__init__() yönteminde yerel fuel_cap değişkenini fuel_cap örneğine ayarladığınız anda, Descriptor sınıfının __set__() yöntemi çağrılır.

Şimdi yakıt kapasitesini negatif bir değere değiştirelim ve programın bir ValueError istisnası yükseltip yükseltmediğine bakalım.

car2 = Car("BMW","X7",-40)
print(car2)
-40



----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-115-1c3d23be72f7> in <module>
----> 1 car2 = Car("BMW","X7",-40)
      2 print(car2)


<ipython-input-107-3e1f3e97d3c7> in __init__(self, make, model, fuel_cap)
      4         self.make = make
      5         self.model = model
----> 6         self.fuel_cap = fuel_cap
      7
      8     def __str__(self):


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
     11
     12         if value < 0:
---> 13             raise ValueError("Fuel Capacity can never be less than zero")
     14
     15         self.__fuel_cap = value


ValueError: Fuel Capacity can never be less than zero

burada hatırlarsanız, öznitelik değerini daha sonra negatif bir sayıya değiştirdiğinizde tip denetimi başarısız olmuştu; çünkü tip denetimi yalnızca __init__() yönteminde bir kez uygulanıyordu. Şimdi fuel_cap değerini bir string’e güncelleyelim ve hata verip vermediğine bakalım.

car2.fuel_cap = -1
-1



----------------------------------------

ValueErrorTraceback (most recent call last)

<ipython-input-120-dea9dbe96ebe> in <module>
----> 1 car2.fuel_cap = -1


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
     11
     12         if value < 0:
---> 13             raise ValueError("Fuel Capacity can never be less than zero")
     14
     15         self.__fuel_cap = value


ValueError: Fuel Capacity can never be less than zero
car2.fuel_cap = "BMW"
----------------------------------------

TypeErrorTraceback (most recent call last)

<ipython-input-121-0b316a9872c6> in <module>
----> 1 car2.fuel_cap = "BMW"


<ipython-input-106-0b252695aeed> in __set__(self, instance, value)
      8             print(value)
      9         else:
---> 10             raise TypeError("Fuel Capacity can only be an integer")
     11
     12         if value < 0:


TypeError: Fuel Capacity can only be an integer

Mükemmel! Gördüğünüz gibi, yakıt kapasitesi özniteliğini daha sonra güncellediğinizde de çalışıyor.

Ancak Tanımlayıcılarda küçük bir sorun var: Sınıfın yeni bir örneğini (ikinci bir örnek) oluşturduğunuzda, önceki örneğin değeri üzerine yazılıyor. Bunun nedeni, Tanımlayıcıların örneğe değil sınıfa bağlı olmasıdır.

Bunu aşağıdaki örnekle anlayalım.

car3 = Car("BMW","X7",48) #created a new instance 'car3' with different values
48

car2 örneğini yazdırdığınızda, değerlerin car3 tarafından üzerine yazıldığını gözlemleyeceksiniz.

print(car2)
BMW model X7 with a fuel capacity of 48 ltr.

Sonuç

Eğitimi tamamladığınız için tebrikler.

Bu eğitim, Python’a hâkim olan ve ileri seviyede ustalaşmayı hedefleyenler içindi. Güzel bir alıştırma olarak, bugün bahsedilen örnek üzerine yazma (instance overriding) sorununu nasıl çözebileceğinizi araştırmak isteyebilirsiniz.

Bu eğitimle ilgili tüm sorularınızı aşağıdaki yorumlar bölümünde sormaktan çekinmeyin.

Python hakkında daha fazla bilgi edinmek isterseniz, DataCamp’in kapsamlı Python Programming beceri yolunu inceleyin.

Python Fonksiyonları Eğitimimize göz atın.

Konular
Python

Python Kursları

Kurs

Python’a Giriş

4 sa
7M
Python ile veri analizi temellerini sadece dört saatte öğrenin. Bu çevrimiçi kurs, Python arayüzünü tanıtacak ve popüler paketleri keşfedecektir.
Ayrıntıları GörüntüleRight Arrow
Kursa Başla
Devamını GörRight Arrow
İlgili

blog

2026’da En Popüler 40 Yazılım Mühendisi Mülakat Sorusu

Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Dario Radečić's photo

Dario Radečić

15 dk.

blog

Hızlı Sevkiyat İçin Pratik Vibe Kodlama Teknoloji Yığını

Ön uç, arka uç, veritabanları, kimlik doğrulama, depolama, e-posta, test, dağıtım ve izleme için en iyi araçları keşfedin.
Abid Ali Awan's photo

Abid Ali Awan

14 dk.

Eğitim

Python'da Listeyi String'e Nasıl Dönüştürürsünüz

Bu hızlı eğitimde, Python'da bir listeyi string'e nasıl dönüştüreceğinizi öğrenin.
Adel Nehme's photo

Adel Nehme

Eğitim

.gitignore Nasıl Kullanılır: Örneklerle Pratik Bir Giriş

Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
Kurtis Pykes 's photo

Kurtis Pykes

8 dk.

Devamını GörDevamını Gör