koddla

Yazılımcıları bilgi ile güçlendirir.

Python Class – Sınıf

Python kendisini yalnızca popüler bir komut dosyası dili olarak sunmakla kalmaz, aynı zamanda nesne yönelimli programlama paradigmasını da destekler. Sınıflar verileri tanımlar ve hepsi tek bir nesne altında toplanan bu verileri işlemek için yöntemler sağlar. Ayrıca sınıflar, somut uygulama ayrıntılarını verilerin soyut temsillerinden ayırarak soyutlamaya izin verir.

Sınıf kullanan kodun okunması, anlaşılması ve bakımı genellikle daha kolaydır.

Sınıf kavramına giriş

Bir sınıf, belirli bir nesnenin temel özelliklerini tanımlayan bir şablon olarak işlev görür:

class Person(object):
     """Basit bir sınıf."""                            # docstring
     species = "Homo Sapiens"                         # sınıf özniteliği

     def __init__(self, name):                        # özel metot
         """Bu başlatıcıdır. Bu özel bir
         yöntemdir (aşağıya bakın).
         """
         self.name = name                             # örnek özniteliği

     def __str__(self):                               # özel yöntem
         """Bu metot, Python aşağıdakileri denediğinde çalıştırılır; 
         nesneyi bir dizeye dönüştürmek için veya print() vb. işlevleri kullanırken bu dizeyi döndürmek için.
         """
         return self.name

     def rename(self, renamed):                       # normal yöntem
         """Ad niteliğini yeniden atayın ve yazdırın.""
         self.name = renamed
         print("Şimdi benim adım {}".format(self.name))

Yukarıdaki örneğe bakarken dikkat edilmesi gereken birkaç husus vardır.

  1. Sınıf, nitelikler (veriler) ve yöntemlerden (işlevler) oluşur.
  2. Nitelikler ve yöntemler basitçe normal değişkenler ve işlevler olarak tanımlanır.
  3. İlgili docstring’de belirtildiği gibi, init() yöntemi başlatıcı olarak adlandırılır. Diğer nesne yönelimli dillerdeki constructor’a eşdeğerdir ve yeni bir nesne veya sınıfın yeni bir örneğini oluşturduğunuzda ilk çalıştırılan yöntemdir.
  4. Tüm sınıf için geçerli olan nitelikler ilk olarak tanımlanır ve sınıf nitelikleri olarak adlandırılır.
  5. Bir sınıfın belirli bir örneği (bir nesne) için geçerli olan niteliklere örnek nitelikleri denir. Genellikle init() içinde tanımlanırlar; bu gerekli değildir, ancak önerilir (init() dışında tanımlanan nitelikler tanımlanmadan önce erişilme riski taşıdığından).
  6. Sınıf tanımına dahil edilen her yöntem, söz konusu nesneyi ilk parametresi olarak geçirir. Bu parametre için self kelimesi kullanılır (self kelimesinin Python’da kendine özgü bir anlamı olmadığından, self kullanımı aslında gelenekseldir, ancak bu Python’un en saygın geleneklerinden biridir ve her zaman buna uymalısınız).
  7. Diğer dillerde nesne yönelimli programlamaya alışkın olanlar birkaç şeye şaşırabilirler. Birincisi, Python’un gerçek bir private öğe kavramı yoktur, bu nedenle her şey varsayılan olarak C++/Java public anahtar sözcüğünün davranışını taklit eder.
  8. Sınıfın bazı yöntemleri aşağıdaki biçime sahiptir: functionname(self, other_stuff). Bu tür yöntemlerin tümüne “sihirli yöntemler” denir ve Python’da sınıfların önemli bir parçasıdır. Örneğin, Python’da operatör aşırı yükleme sihirli metotlarla gerçekleştirilir.

Şimdi Person sınıfımızın birkaç nesnesini oluşturalım!

>>> # nesneler
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")

Şu anda kelly, joseph ve john_doe olmak üzere üç Person nesnemiz var.

Nokta operatörünü kullanarak her bir örnekten sınıfın niteliklerine erişebiliriz. Sınıf ve örnek öznitelikleri arasındaki farka tekrar dikkat edin:

>>> # Nitelikler
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'

Aynı nokta operatörünü kullanarak sınıfın metotlarını çalıştırabiliriz:

>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>>  print(john_doe)
'John Doe'
>>>  john_doe.rename("John")
'Now my name is John'

Maymun Yaması – Monkey Patching

Bu durumda, “maymun yaması”, bir sınıfa tanımlandıktan sonra yeni bir değişken veya yöntem eklemek anlamına gelir. Örneğin, A sınıfını şu şekilde tanımladığımızı varsayalım

class A(object):
    def __init__(self, num):
        self.num = num

    def __add__(self, other):
        return A(self.num + other.num)

Ancak şimdi kodun ilerleyen kısımlarına başka bir fonksiyon eklemek istiyoruz. Bu fonksiyonun aşağıdaki gibi olduğunu varsayalım.

def get_num(self):
    return self.num

Peki bunu A’ya bir yöntem olarak nasıl ekleyeceğiz? Çok basit, aslında bu fonksiyonu bir atama deyimiyle A’ya yerleştiriyoruz.

A.get_num = get_num

Bu neden işe yarıyor? Çünkü fonksiyonlar tıpkı diğer nesneler gibi nesnelerdir ve metotlar sınıfa ait fonksiyonlardır.

get_num işlevi tüm mevcut (önceden oluşturulmuş) ve yeni A örnekleri için kullanılabilir olmalıdır.

Bu eklemeler, söz konusu sınıfın (veya alt sınıflarının) tüm örneklerinde otomatik olarak kullanılabilir. Örneğin:

foo = A(42)

A.get_num = get_num

bar = A(6);

foo.get_num() # 42

bar.get_num() # 6

Diğer bazı dillerin aksine, bu tekniğin belirli yerleşik türler için çalışmadığını ve iyi bir stil olarak kabul edilmediğini unutmayın.

Yeni tarz ve eski tarz sınıflar

Python 2.2’de sınıfları ve türleri birleştirmek için yeni stil sınıflar tanıtıldı. En üst düzey nesne türünden miras alırlar. Yeni tarz bir sınıf kullanıcı tanımlı bir türdür ve yerleşik türlere çok benzer.

# yeni stil sınıf
class New(object):
    pass

# yeni tarz örnek
new = New()

new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True

Eski tarz sınıflar nesneden miras almaz. Eski tarz örnekler her zaman yerleşik bir örnek türüyle uygulanır.

# eski tarz sınıf
class Old:
    pass

# eski tarz örnek
old = Old()

old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False

Python 3’te eski tarz sınıflar kaldırıldı.

Python 3’teki yeni stil sınıflar object’ten dolaylı olarak miras alır, bu nedenle artık MyClass(object) belirtmeye gerek yoktur.

class MyClass:
    pass

my_inst = MyClass()

type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True

Bağlı/bağlı olmayan ve statik yöntemler

Bağlı ve bağlı olmayan metotlar fikri Python 3’te kaldırılmıştır. Python 3’te bir sınıf içinde bir yöntem bildirdiğinizde, bir def anahtar sözcüğü kullanırsınız, böylece bir işlev nesnesi oluşturursunuz. Bu normal bir fonksiyondur ve çevreleyen sınıf onun isim alanı olarak çalışır. Aşağıdaki örnekte A sınıfı içinde f metodunu bildiriyoruz ve bu A.f fonksiyonuna dönüşüyor:

class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

Python 2’de davranış farklıydı: sınıf içindeki fonksiyon nesneleri, belirli bir sınıf örneğine bağlı olmadıkları için bağlı olmayan yöntemler olarak adlandırılan instancemethod türündeki nesnelerle dolaylı olarak değiştirildi. Temel fonksiyona .func özelliğini kullanarak erişmek mümkündü.

A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

Bu son davranışlar inspect ile doğrulanır – Python 3’te yöntemler işlev olarak tanınırken, Python 2’de bu ayrım korunur.

import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False

Python 3:

import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

Python’un her iki sürümünde de A.f fonksiyonu/yöntemi, ilk argüman olarak A sınıfının bir örneğini geçirmeniz koşuluyla doğrudan çağrılabilir.

A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
#                      A instance as first argument (got int instance instead) 
# Python 3: 14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

Şimdi a’nın A sınıfının bir nesnesi olduğunu varsayalım, o zaman a.f nedir? Sezgisel olarak bu, A sınıfının aynı f metodu olmalıdır, sadece bir şekilde a nesnesine uygulandığını “bilmelidir” – Python’da buna a’ya bağlı metot denir.

Özlü ayrıntılar şu şekildedir: a.f yazmak a’nın sihirli __getattribute__ yöntemini çağırır, bu yöntem önce a’nın f adında bir özniteliğe sahip olup olmadığını kontrol eder (sahip değildir), ardından A sınıfının böyle bir ada sahip bir yöntem içerip içermediğini kontrol eder (içerir) ve m.__func__ içinde orijinal A.f’ye ve m.__self__ içinde a nesnesine bir referansı olan yöntem türünde yeni bir m nesnesi oluşturur. Bu nesne bir fonksiyon olarak çağrıldığında, basitçe şunu yapar: m(…) => m.__func__(m.__self__, …). Böylece bu nesneye bağlı bir yöntem denir çünkü çağrıldığında ilk argüman olarak bağlı olduğu nesneyi sağlamayı bilir. (Bu şeyler Python 2 ve 3’te de aynı şekilde çalışır).

a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4

#Not: a.f bağlı yöntem nesnesi *her çağırışınızda* yeniden oluşturulur:
a.f is a.f # False
# Bir performans optimizasyonu olarak, bağlı yöntemi nesnenin __dict__ değerinde saklayabiliriz, bu durumda yöntem nesnesi sabit kalacaktır:
a.f = a.f
a.f is a.f  # True

Son olarak, Python’da sınıf metotları ve statik metotlar vardır – özel metot türleri. Sınıf yöntemleri normal yöntemlerle aynı şekilde çalışır, ancak bir nesne üzerinde çağrıldıklarında nesne yerine nesnenin sınıfına bağlanırlar. Böylece m.__self__ = type(a) olur. Böyle bir bağlı yöntemi çağırdığınızda, ilk argüman olarak a’nın sınıfını geçirir. Statik metotlar daha da basittir: hiçbir şeye bağlanmazlar ve herhangi bir dönüştürme yapmadan sadece temel fonksiyonu döndürürler.

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        return cls.multiplier * x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world

Sınıf yöntemlerinin, örneğe erişildiğinde bile sınıfa bağlı olduğunu unutmayın:

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20

En düşük seviyede, fonksiyonların, metotların, staticmetotların vb. aslında get, set ve isteğe bağlı olarak del özel metotlarını çağıran tanımlayıcılar olduğunu belirtmek gerekir.

Sınıf yöntemleri – alternatif başlatıcılar

Sınıf yöntemleri, sınıfların örneklerini oluşturmak için alternatif yollar sunar. Açıklamak için bir örneğe bakalım.

Nispeten basit bir Person sınıfımız olduğunu varsayalım:

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

Bu sınıfın örneklerini ad ve soyadı ayrı ayrı belirtmek yerine tam adı belirterek oluşturmanın bir yolu olması kullanışlı olabilir. Bunu yapmanın bir yolu, last_name’in isteğe bağlı bir parametre olması ve verilmediği takdirde tam adın geçildiğini varsaymak olabilir:

class Person(object):

    def __init__(self, first_name, age, last_name=None):
        if last_name is None:
            self.first_name, self.last_name = first_name.split(" ", 2)
        else:
            self.first_name = first_name
            self.last_name = last_name
        
        self.full_name = self.first_name + " " + self.last_name
        self.age = age

    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

Ancak, bu kod parçasıyla ilgili iki ana sorun vardır:

  1. First_name ve last_name parametreleri artık yanıltıcıdır, çünkü first_name için tam bir ad girebilirsiniz. Ayrıca, bu tür esnekliğe sahip daha fazla durum ve/veya daha fazla parametre varsa, if/elif/else dallanmaları hızla can sıkıcı hale gelebilir.
  2. O kadar önemli değil ama yine de belirtmekte fayda var: ya last_name None ise ama first_name boşluklarla iki ya da daha fazla şeye bölünmüyorsa? Başka bir girdi doğrulama ve/veya istisna işleme katmanımız daha oldu…

Sınıf yöntemlerine başvuralım. Tek bir başlatıcı yerine, from_full_name adında ayrı bir başlatıcı oluşturacağız ve bunu (yerleşik) classmethod dekoratörü ile süsleyeceğiz.

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    @classmethod
    def from_full_name(cls, name, age):
        if " " not in name:
            raise ValueError
        first_name, last_name = name.split(" ", 2)
        return cls(first_name, last_name, age)
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

from_full_name’in ilk argümanı olarak self yerine cls’ye dikkat edin. Sınıf yöntemleri, belirli bir sınıfın örneğine değil (self genellikle bunu ifade eder), sınıfın tamamına uygulanır. Dolayısıyla, eğer cls bizim Person sınıfımızsa, from_full_name sınıf metodundan dönen değer Person(first_name, last_name, age) olur ve Person sınıfının bir örneğini oluşturmak için Person’ın init özelliğini kullanır. Özellikle, Person sınıfının Employee alt sınıfını oluşturacak olursak, from_full_name Employee sınıfında da çalışacaktır.

Bunun beklendiği gibi çalıştığını göstermek için, init‘de dallanma olmadan birden fazla şekilde Person örnekleri oluşturalım:

In [2]: bob = Person("Bob", "Bobberson", 42)

In [3]: alice = Person.from_full_name("Alice Henderson", 31)

In [4]: bob.greet()
Hello, my name is Bob Bobberson.

In [5]: alice.greet()
Hello, my name is Alice Henderson.

Default values for instance variables

Değişken değişmez türde bir değer içeriyorsa (örneğin bir dize), aşağıdaki gibi varsayılan bir değer atamakta bir sakınca yoktur

class Rectangle(object):
    def __init__(self, width, height, color='blue'):
        self.width = width
        self.height = height
        self.color = color
    
    def area(self):
        return self.width  * self.height 

# Sınıfın bazı örneklerini oluşturun
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue

red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red

Oluşturma işleminde liste gibi değiştirilebilir nesneleri başlatırken dikkatli olmak gerekir. Aşağıdaki örneği ele alalım:

class Rectangle2D(object):
    def __init__(self, width, height, pos=[0,0], color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2'nin konumu da değişti

Bu davranış, Python’da varsayılan parametrelerin işlev bildiriminde değil, işlev yürütülürken bağlanması gerçeğinden kaynaklanır. Örnekler arasında paylaşılmayan bir varsayılan örnek değişkeni elde etmek için, aşağıdaki gibi bir yapı kullanılmalıdır:

class Rectangle2D(object):
    def __init__(self, width, height, pos=None, color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos or [0, 0] # varsayılan değer [0, 0]
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2'nin konumu da değişti

Sınıf yapısı

Sınıf kompozisyonu nesneler arasında açık ilişkilere izin verir. Aşağıdaki örnekte, insanlar ülkelere ait şehirlerde yaşamaktadır. Kompozisyon, insanların kendi ülkelerinde yaşayan tüm insanların sayısına erişmelerini sağlar:

class Country(object):
    def __init__(self):
        self.cities=[]
        
    def addCity(self,city):
        self.cities.append(city)
        

class City(object):
    def __init__(self, numPeople):
        self.people = []
        self.numPeople = numPeople
        
        
    def addPerson(self, person):
        self.people.append(person)
    
    def join_country(self,country):
        self.country = country
        country.addCity(self)
        
        for i in range(self.numPeople):
                person(i).join_city(self)
  

class Person(object):
    def __init__(self, ID):
        self.ID=ID

    def join_city(self, city):
        self.city = city
        city.addPerson(self)
        
    def people_in_my_country(self):
        x= sum([len(c.people) for c in self.city.country.cities])
        return x
        
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)

print(US.cities[0].people[0].people_in_my_country())

# 15

Sınıf ve örnek değişkenleri

Örnek değişkenleri her örnek için benzersizdir, sınıf değişkenleri ise tüm örnekler tarafından paylaşılır.

class C:
    x = 2  # class değişkeni

    def __init__(self, y):
        self.y = y  # instance değişkeni

C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'

c1 = C(3)
c1.x
# 2
c1.y
# 3

c2 = C(4)
c2.x
# 2
c2.y
# 4

Sınıf değişkenlerine bu sınıfın örneklerinden erişilebilir, ancak sınıf niteliğine atama yapmak, sınıf değişkenini gölgeleyen bir örnek değişken yaratacaktır

c2.x = 4
c2.x
# 4
C.x
# 2

Sınıf değişkenlerini örneklerden değiştirmenin bazı beklenmedik sonuçlara yol açabileceğini unutmayın.

class D:
    x = []
    def __init__(self, item):
        self.x.append(item)  # bunun bir tanımlama olmadiğini unutmayin!

d1 = D(1)
d2 = D(2)

d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]

Tüm Sınıf Üyelerini Listeleme

dir() fonksiyonu, bir sınıfın üyelerinin bir listesini almak için kullanılabilir:

dir(Class)

Örneğin:

>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Yalnızca “sihirli olmayan” üyeleri aramak yaygındır. Bu, __ ile başlamayan isimlere sahip üyeleri listeleyen basit bir kavrama kullanılarak yapılabilir:

>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Uyarılar:

Sınıflar bir __dir__() metodu tanımlayabilir. Eğer bu metot mevcutsa dir() metodu __dir__() metodunu çağırır, aksi takdirde Python sınıfın üyelerinin bir listesini oluşturmaya çalışır. Bu, dir fonksiyonunun beklenmedik sonuçlar doğurabileceği anlamına gelir. Resmi python dokümantasyonundan iki önemli alıntıya bakalım:

Nesne dir() işlevini sağlamazsa, işlev, tanımlanmışsa nesnenin dict niteliğinden ve tür nesnesinden bilgi toplamak için elinden gelenin en iyisini yapmaya çalışır. Sonuçta elde edilen liste tam olmayabilir ve nesnenin özel bir getattr() işlevi varsa hatalı olabilir.

Not: dir() öncelikle etkileşimli bir komut isteminde kullanım için bir kolaylık olarak sağlandığından, titizlikle veya tutarlı bir şekilde tanımlanmış bir ad kümesi sağlamaya çalışmaktan çok daha ilginç bir ad kümesi sağlamaya çalışır ve ayrıntılı davranışı sürümler arasında değişebilir. Örneğin, argüman bir sınıf olduğunda metasınıf nitelikleri sonuç listesinde yer almaz.

ABCMeta üst sınıfını ayarlama

Abstract sınıflar, miras alınması amaçlanan ancak belirli yöntemleri uygulamaktan kaçınan, yalnızca alt sınıfların uygulaması gereken yöntem imzalarını geride bırakan sınıflardır.

Soyut sınıflar, typed dillerdeki arayüz kavramına benzer şekilde, yöntem uygulamasına ihtiyaç duymadan sınıf soyutlamalarını yüksek seviyede tanımlamak ve uygulamak için kullanışlıdır.

Soyut bir sınıf tanımlamak için kavramsal bir yaklaşım, sınıf yöntemlerini saplamak ve ardından erişilirse NotImplementedError yükseltmektir. Bu, alt sınıfların soyut sınıfları geçersiz kılmadan üst yöntemlere erişmesini önler. Bunun gibi:

class Fruit:
    
    def check_ripeness(self):
        raise NotImplementedError("check_ripeness method not implemented!")
class Apple(Fruit):
    pass

a = Apple()
a.check_ripeness() #  NotImplementedError

Bu şekilde bir soyut sınıf oluşturmak, geçersiz kılınmayan yöntemlerin uygunsuz kullanımını önler ve kesinlikle yöntemlerin alt sınıflarda tanımlanmasını teşvik eder, ancak bunların tanımlanmasını zorunlu kılmaz. abc modülü ile, ebeveynlerinin ve atalarının soyut sınıf yöntemlerini geçersiz kılmadıklarında çocuk sınıfların örneklenmesini önleyebiliriz:

from abc import ABCMeta

class AbstractClass(object):
    # metasınıf niteliği her zaman bir sınıf değişkeni olarak ayarlanmalıdır 
    __metaclass__ = ABCMeta

   # abstractmethod dekoratörü bu yöntemi tanımsız olarak kaydeder
   @abstractmethod 
   def virtual_method_subclasses_must_define(self):
       # Tamamen boş bırakılabilir veya bir temel uygulama sağlanabilir
       #Normalde boş bir yorumlamanın dolaylı olarak `None` döndürdüğünü unutmayın, ancak kayıt yaptırıldığında bu davranış artık zorunlu değildir.

Artık basitçe alt sınıf oluşturmak ve geçersiz kılmak mümkündür:

class Subclass(AbstractClass):
    def virtual_method_subclasses_must_define(self):
        return

Singleton sınıfı

Singleton, bir sınıfın örneklenmesini tek bir örnek/nesne ile sınırlayan bir kalıptır. Python singleton tasarım kalıpları hakkında daha fazla bilgi için buraya bakın.

class Singleton:
    def __new__(cls):
        try:
            it = cls.__it__
        except AttributeError:
            it = cls.__it__ = object.__new__(cls)
        return it

    def __repr__(self):
        return '<{}>'.format(self.__class__.__name__.upper())

    def __eq__(self, other):
         return other is self

Başka bir yöntem de sınıfınızı süslemektir:

class Singleton:
    """
    Singleton'ları uygulamayı kolaylaştırmak için thread-safe olmayan bir yardımcı sınıf. Bu sınıf, singleton olması gereken sınıf için bir üst sınıf olarak değil, bir dekoratör olarak kullanılmalıdır.

Dekoratör sınıf sadece `self` argümanını alan bir `__init__` fonksiyonu tanımlayabilir. Bunun dışında, dekore edilmiş sınıf için geçerli olan herhangi bir kısıtlama yoktur.

    Singleton örneğini almak için `Instance` yöntemini kullanın. `__call__` kullanmaya çalışmak bir `TypeError` oluşmasına neden olur.

    Sınırlamalar: Süslenmiş sınıf miras alınamaz.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Singleton örneğini döndürür. İlk çağrıldığında, bir dekore edilmiş sınıfın yeni bir örneğini oluşturur ve `__init__` yöntemini çağırır. Sonraki tüm çağrılarda, önceden oluşturulmuş örnek döndürülür.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

Kullanmak için Instance yöntemini kullanabilirsiniz

@Singleton
class Single:
    def __init__(self):
        self.name=None
        self.val=0
    def getName(self):
        print(self.name)

x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # çıktı: I'm single
y.getName() # çıktı: I'm single

Temel miras/kalıtım

suggest change

Python’daki kalıtım Java, C++ gibi diğer nesne yönelimli dillerde kullanılan benzer fikirlere dayanır. Yeni bir sınıf mevcut bir sınıftan aşağıdaki şekilde türetilebilir.

class BaseClass(object):
    pass

class DerivedClass(BaseClass):
    pass

BaseClass zaten var olan (ebeveyn) sınıftır ve DerivedClass, BaseClass’tan nitelikleri miras alan (veya alt sınıf oluşturan) yeni (çocuk) sınıftır. Not: Python 2.2’den itibaren tüm sınıflar, tüm yerleşik türler için temel sınıf olan object sınıfından örtük olarak miras alır.

Aşağıdaki örnekte, object’ten örtük olarak miras alan bir üst Rectangle sınıfı tanımlıyoruz:

class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h
    
    def area(self):
        return self.w * self.h
        
    def perimeter(self):
        return 2 * (self.w + self.h)

Rectangle sınıfı, Square sınıfını tanımlamak için bir temel sınıf olarak kullanılabilir, çünkü kare, dikdörtgenin’ın özel bir durumudur.

class Square(Rectangle):
    def __init__(self, s):
        # ana oluşturucuyu çağırır, w ve h'nin her ikisi de s'ye eşit
        super(Square, self).__init__(s, s)
        self.s = s

Square sınıfı otomatik olarak Rectangle sınıfının tüm niteliklerini ve object sınıfını miras alacaktır. super(), Rectangle sınıfının __init__() yöntemini çağırmak için kullanılır, aslında temel sınıfın herhangi bir geçersiz kılınmış yöntemini çağırır. Not: Python 3’te super() argüman gerektirmez.

Türetilmiş sınıf nesneleri, temel sınıflarının niteliklerine erişebilir ve bunları değiştirebilir:

r.area()
# Çıktı: 12
r.perimeter()  
# Çıktı: 14

s.area()
# Çıktı: 4
s.perimeter()
# Çıktı: 8

Kalıtım ile çalışan yerleşik fonksiyonlar 

issubclass(DerivedClass, BaseClass): DerivedClass, BaseClass’ın bir alt sınıfıysa True döndürür

isinstance(s, Class): s, Class’ın veya Class’tan türetilmiş sınıfların bir örneğiyse True döndürür

# alt sınıf kontrolü        
issubclass(Square, Rectangle)
# Çıktı: True

# instantiate
r = Rectangle(3, 4)
s = Square(2)

isinstance(r, Rectangle)  
# Çıktı: True
isinstance(r, Square)
# Çıktı: False
# Dikdörtgen bir kare değildir

isinstance(s, Rectangle)
# Çıktı: True
# Kare bir dikdörtgendir
isinstance(s, Square)
# Çıktı: True

@Property – Özellikler

Python sınıfları, normal nesne değişkenleri gibi görünen, ancak özel davranış ve dokümantasyon ekleme olasılığı olan özellikleri destekler.

class MyClass(object):

    def __init__(self):
       self._my_string = ""
    
    @property
    def string(self):
        """Son derece önemli bir dize."""
        return self._my_string

    @string.setter
    def string(self, new_value):
        assert isinstance(new_value, str), \
               "Bana bir dize verin, %r değil!" type(new_value)
        self._my_string = new_value

    @string.deleter
    def x(self):
        self._my_string = None

MyClass sınıfının nesneleri .string özelliğine sahip gibi görünecektir, ancak davranışı artık sıkı bir şekilde kontrol edilmektedir:

mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string

Yukarıdaki gibi kullanışlı sözdiziminin yanı sıra, özellik sözdizimi bu niteliklere doğrulama veya diğer eklentilerin eklenmesine olanak tanır. Bu, özellikle kullanıcıya belirli bir düzeyde yardım verilmesi gereken genel API’lerde faydalı olabilir.

Özelliklerin bir başka yaygın kullanımı da sınıfın ‘sanal nitelikler’ sunmasını sağlamaktır – aslında saklanmayan ancak yalnızca istendiğinde hesaplanan nitelikler.

class Character(object):
    def __init__(name, max_hp):
        self._name = name
        self._hp = max_hp
        self._max_hp = max_hp

    # Bir set yöntemi sağlamayarak hp'yi yalnızca okunur hale getirin
    @property
    def hp(self):
        return self._hp

  # Bir set yöntemi sağlamayarak name'i yalnızca okunur hale getirin
    @property
    def name(self):
        return self.name

    def take_damage(self, damage):
        self.hp -= damage
        self.hp = 0 if self.hp <0 else self.hp

    @property
    def is_alive(self):
        return self.hp != 0

    @property
    def is_wounded(self):
        return self.hp < self.max_hp if self.hp > 0 else False

    @property
    def is_dead(self):
        return not self.is_alive

bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# Çıktı : 100
bilbo.hp = 200        
# Çıktı: AttributeError: can't set attribute
# hp niteliği yalnızca okunur.

bilbo.is_alive
# Çıktı : True
bilbo.is_wounded
# Çıktı : False
bilbo.is_dead
# Çıktı : False

bilbo.take_damage( 50 )

bilbo.hp
# Çıktı : 50

bilbo.is_alive
# Çıktı : True
bilbo.is_wounded
# Çıktı : True
bilbo.is_dead
# Çıktı : False

bilbo.take_damage( 50 )
bilbo.hp
# Çıktı : 0

bilbo.is_alive
# Çıktı : False
bilbo.is_wounded
# Çıktı : False
bilbo.is_dead
# Çıktı : True

Özellik dekoratörünün kullanımı

Özellik dekoratörü, bir sınıfta nitelikler gibi davranan yöntemleri tanımlamak için kullanılabilir. Bunun yararlı olabileceği bir örnek, başlangıçta (pahalı) bir arama ve daha sonra basit bir erişim gerektirebilecek bilgilerin açığa çıkarılmasıdır.

foobar.py modülü verildiğinde:

class Foo(object):
	def **init**(self):
		self.__bar = None
	
	@property
	def bar(self):
		if self.__bar is None:
			self.__bar = pahalı\bir\arama\işlevi()
		return self.__bar

Sonra da:

>>> from foobar import Foo
>>> foo = Foo()
>>> print(foo.bar)  # Başlatma işleminden sonra bar None olduğundan bu işlem biraz zaman alacaktır
42
>>> print(foo.bar)  # Bar artık bir değere sahip olduğundan bu çok daha hızlıdır
42

Okuma-yazma özellikleri için özellik dekoratörünü kullanma

Set ve get için özel davranış uygulamak üzere @property kullanmak istiyorsak, aşağıdaki kalıbı kullanırız:

class Cash(object):
    def __init__(self, value):
        self.value = value
    @property
    def formatted(self):
        return '${:.2f}'.format(self.value)
    @formatted.setter
    def formatted(self, new):
        self.value = float(new[1:])

Kullanımı:

>>> wallet = Cash(2.50)
>>> print(wallet.formatted)
$2.50
>>> print(wallet.value)
2.5
>>> wallet.formatted = '$123.45'
>>> print(wallet.formatted)
$123.45
>>> print(wallet.value)
123.45

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Back to top