Nesne Yönelimli Python - Gelişmiş Özellikler

Burada Python'un sağladığı bazı gelişmiş özelliklere bakacağız.

Sınıf tasarımımızda Temel Sözdizimi

Burada Python'un sınıflarımızdaki operatörlerden yararlanmamıza nasıl izin verdiğini inceleyeceğiz. Python, büyük ölçüde nesneler ve yöntemler çağrısı yapan nesnelerdir ve bu, bazı uygun sözdizimiyle gizlendiğinde bile devam eder.

>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']

Yani kendi sınıflarımıza __add__ sihirli yöntemini eklememiz gerekirse, bunu da yapabilir miyiz? Bunu yapmaya çalışalım.

Listeyi my_list adında bir argüman olarak alan bir __init__ oluşturucusuna sahip olan Sumlist adında bir sınıfımız var.

class SumList(object):
   def __init__(self, my_list):
      self.mylist = my_list
   def __add__(self, other):
     new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]

     return SumList(new_list)
   
   def __repr__(self):
      return str(self.mylist)

aa = SumList([3,6, 9, 12, 15])

bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])

Çıktı

[103, 206, 309, 412, 515]

Ancak diğer sihirli yöntemlerle dahili olarak yönetilen birçok yöntem vardır. Aşağıda bunlardan bazıları var

'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()

Yerleşik türlerden devralma

Sınıflar ayrıca yerleşik türlerden miras alabilir, bu, herhangi bir yerleşikten miras aldığı ve orada bulunan tüm işlevlerden yararlanabileceği anlamına gelir.

Aşağıdaki örnekte sözlükten miras alıyoruz, ancak daha sonra __setitem__ yöntemlerinden birini uyguluyoruz. Bu (setitem), sözlükte anahtar ve değer belirlediğimizde çağrılır. Bu sihirli bir yöntem olduğu için, buna dolaylı olarak çağrılacaktır.

class MyDict(dict):

   def __setitem__(self, key, val):
      print('setting a key and value!')
      dict.__setitem__(self, key, val)

dd = MyDict()
dd['a'] = 10
dd['b'] = 20

for key in dd.keys():
   print('{0} = {1}'.format(key, dd[key]))

Çıktı

setting a key and value!
setting a key and value!
a = 10
b = 20

Bir önceki örneğimizi genişletelim, aşağıda liste indeksi ile uğraşırken daha iyi çağrılan __getitem__ ve __setitem__ adlı iki sihirli yöntemi çağırdık.

# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
   def __getitem__(self, index):
      if index == 0:
         raise IndexError
      if index > 0:
         index = index - 1
         return list.__getitem__(self, index) # this method is called when

# we access a value with subscript like x[1]
   def __setitem__(self, index, value):
      if index == 0:
         raise IndexError
      if index > 0:
      index = index - 1
      list.__setitem__(self, index, value)

x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list

print(x) # __repr__() inherited from builtin list

x.append('HELLO'); # append() inherited from builtin list

print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
               # method. index is 1, but reflects 0!

print (x[4]) # 'HELLO' (index is 4 but reflects 3!

Çıktı

['a', 'b', 'c']
a
HELLO

Yukarıdaki örnekte, Mylist'te üç öğe listesi oluşturuyoruz ve örtük olarak __init__ yöntemi çağrılıyor ve x öğesini yazdırdığımızda, üç öğe listesi (['a', 'b', 'c']) elde ediyoruz. Sonra bu listeye başka bir öğe ekliyoruz. Daha sonra indeks 1 ve indeks 4'ü istiyoruz. Ama çıktıyı görürseniz, istediğimizden (indeks-1) elementi alıyoruz. Bildiğimiz gibi liste indeksleme 0'dan başlıyor ama burada indeksleme 1'den başlıyor (bu yüzden listenin ilk öğesini alıyoruz).

Adlandırma Kuralları

Burada değişkenler için, özellikle de özel değişkenler için kullanacağımız isimleri ve dünya çapında Python programcıları tarafından kullanılan kuralları inceleyeceğiz. Değişkenler özel olarak tanımlansa da Python'da gizlilik yoktur ve bu tasarım gereği. Diğer tüm iyi belgelenmiş diller gibi, Python da onları zorlamasa da teşvik ettiği adlandırma ve stil kurallarına sahiptir. Tarafından yazılmış bir stil rehberi var.Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/

PEP, Python geliştirme önerisinin kısaltmasıdır ve önerilen değişiklikleri tartışmak için Python topluluğu arasında dağıtılan bir dizi belgedir. Örneğin herkese tavsiye edilir,

  • Modül adları - all_lower_case
  • Sınıf adları ve istisna adları - CamelCase
  • Global ve yerel isimler - all_lower_case
  • İşlevler ve yöntem adları - all_lower_case
  • Sabitler - ALL_UPPER_CASE

Bunlar sadece öneri, isterseniz değişiklik yapabilirsiniz. Ancak geliştiricilerin çoğu bu önerilere uyduğu için kodunuz daha az okunabilir olabilir.

Neden sözleşmeye uymalı?

Almamıza izin verdiği KEP tavsiyesini takip edebiliriz,

  • Geliştiricilerin büyük çoğunluğuna daha aşina
  • Kodunuzun çoğu okuyucusu için daha anlaşılır.
  • Aynı kod tabanında çalışan diğer katılımcıların stiliyle eşleşecek.
  • Profesyonel bir yazılım geliştiricinin işareti
  • Herkes seni kabul edecek.

Değişken Adlandırma - "Genel" ve "Özel"

Python'da, modüller ve sınıflarla uğraşırken, bazı değişkenleri veya nitelikleri özel olarak belirleriz. Python'da, bir nesnenin içindekiler dışında erişilemeyen "Özel" örnek değişkeninin varlığı yoktur. Özel, basitçe kodun kullanıcıları tarafından kullanılmak üzere tasarlanmadıkları, bunun yerine dahili olarak kullanılmaları amaçlandığı anlamına gelir. Genel olarak, çoğu Python geliştiricisi tarafından bir kuralı takip etmektedir, yani örneğin bir alt çizgi ile ön eklenmiş bir isim. _attrval (aşağıdaki örnek) bir işlev, yöntem veya veri üyesi olsun, API'nin veya herhangi bir Python kodunun herkese açık olmayan bir parçası olarak ele alınmalıdır. Aşağıda, uyguladığımız adlandırma kuralı bulunmaktadır,

  • Genel nitelikler veya değişkenler (bu modülün ithalatçısı veya bu sınıfın kullanıcısı tarafından kullanılması amaçlanmıştır) -regular_lower_case

  • Özel nitelikler veya değişkenler (modül veya sınıf tarafından dahili kullanım) -_single_leading_underscore

  • Alt sınıflandırılmaması gereken özel öznitelikler -__double_leading_underscore

  • Sihirli özellikler -__double_underscores__(kullanın, onları yaratmayın)

class GetSet(object):

   instance_count = 0 # public
   
   __mangled_name = 'no privacy!' # special variable

   def __init__(self, value):
      self._attrval = value # _attrval is for internal use only
      GetSet.instance_count += 1

   @property
   def var(self):
      print('Getting the "var" attribute')
      return self._attrval

   @var.setter
   def var(self, value):
      print('setting the "var" attribute')
      self._attrval = value

   @var.deleter
   def var(self):
      print('deleting the "var" attribute')
      self._attrval = None

cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)

Çıktı

setting the "var" attribute
10
no privacy!