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:
__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.
classCar: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)Altyapı:
BMW model X7with a fuel capacity of-40 ltr.Altyapı:
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.
classCar: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)Altyapı:
----------------------------------------ValueErrorTraceback(most recent call last)<ipython-input-22-1c3d23be72f7>in<module>---->1 car2 =Car("BMW","X7",-40)2print(car2)<ipython-input-20-1e154783588d>in__init__(self, make, model, fuel_cap)5 self.fuel_cap = fuel_cap
6if self.fuel_cap <0:---->7 raise ValueError("Fuel Capacity can never be less than zero")89 def __str__(self):ValueError: Fuel Capacity can never be less than zero
Altyapı:
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.
classCar: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)Altyapı:
-40 model X7with a fuel capacity of40 ltr.Altyapı:
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.
classCar:def__init__(self,make,model,fuel_cap):
self.make = make
self.model = model
self.fuel_cap = fuel_cap
ifisinstance(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)Altyapı:
----------------------------------------ValueErrorTraceback(most recent call last)<ipython-input-34-75b08cba454f>in<module>---->1 car2 =Car(-40,"X7",40)2print(car2)<ipython-input-31-175690bf3b98>in__init__(self, make, model, fuel_cap)7print(self.make)8else:---->9 raise ValueError("Make of the car can never be an integer")1011if self.fuel_cap <0:ValueError: Make of the car can never be an integer
Altyapı:
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.
classCar:def__init__(self,make,model,fuel_cap):
self.make = make
self.model = model
self.fuel_cap = fuel_cap
ifisinstance(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)Altyapı:
-40 model X7with a fuel capacity of40 ltr.Altyapı:
İş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.
classDescriptor:def__init__(self):
self.__fuel_cap =0def__get__(self, instance, owner):return self.__fuel_cap
def__set__(self, instance, value):ifisinstance(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
Altyapı:
classCar:
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)Altyapı:
40BMW model X7with a fuel capacity of40 ltr.Altyapı:
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.
-40----------------------------------------ValueErrorTraceback(most recent call last)<ipython-input-115-1c3d23be72f7>in<module>---->1 car2 =Car("BMW","X7",-40)2print(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
78 def __str__(self):<ipython-input-106-0b252695aeed>in__set__(self, instance, value)1112if value <0:--->13 raise ValueError("Fuel Capacity can never be less than zero")1415 self.__fuel_cap = value
ValueError: Fuel Capacity can never be less than zero
Altyapı:
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.
-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)1112if value <0:--->13 raise ValueError("Fuel Capacity can never be less than zero")1415 self.__fuel_cap = value
ValueError: Fuel Capacity can never be less than zero
Altyapı:
----------------------------------------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)8print(value)9else:--->10 raise TypeError("Fuel Capacity can only be an integer")1112if value <0:TypeError: Fuel Capacity can only be an integer
Altyapı:
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 valuesAltyapı:
BMW model X7with a fuel capacity of48 ltr.Altyapı:
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.
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.
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!
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ınabağlanırve 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,setvedeleteyöntemlerinin yanı sıra başka öznitelikler de içerebilir.Haydi IBM Developer sayfasından ilhamla
get,setvedeleteyö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ı
caradında,make,modelvefuel_capolmak ü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 üzerindeprint()veyastr()fonksiyonu çağrıldığında çalışır.Gördüğünüz gibi, çıktı gayet iyi görünüyor!
Şimdi arabanın yakıt kapasitesini eksi 40 olarak değiştirelim.
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 birifkoş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.Yukarıdaki çıktıda gördüğünüz gibi, yakıt kapasitesi sıfırın altında olduğunda program bir
ValueErroryükselttiği için şu an her şey yolunda.Ancak başka bir sorun daha olabilir; örneğin girilen
yakıt kapasitesiondalık bir değer ya da bir string olabilir. Sadece yakıt kapasitesi değil, arabanınmakevemodeldeğerleri de bir tamsayı olabilir. Bu durumların hiçbirinde program bir istisna yükseltmeyecektir.Yukarıdaki durumu da ele almak için bir başka
ifkoşulu eklemeyi ya da tip denetimi içinisinstanceyönteminden yararlanmayı düşünebilirsiniz.Bu kez hatayı ele almak için yerleşik
isinstanceyöntemini kullanalım.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.
İş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
okumaeriş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.
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.
__init__()yöntemi yerel değişken__fuel_cap’i sıfıra ayarlar. Başındakidunder(ç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.__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 (yanicar2) ve son olarak örneğin ait olduğu sınıf (yaniCarsınıfı). Bu yöntemde,__set__()yönteminde ayarlananfuel_capdeğ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ızvalue. Bu yöntemde,fuel_capözniteliğine atamak istediğiniz değerin tamsayı olup olmadığını kontrol edersiniz. Değilse birTypeErroristisnası yükseltirsiniz. Ardından aynı yöntemde değerin sıfırdan küçük olup olmadığını kontrol eder, küçükse bu kez birValueErroristisnası yükseltirsiniz. Hataları kontrol ettikten sonrafuel_capözniteliğini bu değere güncellersiniz.__delete__()yöntemi, bir öznitelik bir nesneden silindiğinde çağrılır ve__set__()yöntemi gibi bir şey döndürmez.Carsınıfı öncekiyle aynı kalır. Ancak yaptığınız tek değişiklik,Descriptor()sınıfınınfuel_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 yerelfuel_capdeğişkeninifuel_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.
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. Şimdifuel_capdeğerini bir string’e güncelleyelim ve hata verip vermediğine bakalım.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.
car2örneğini yazdırdığınızda, değerlerincar3tarafından üzerine yazıldığını gözlemleyeceksiniz.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.