Ana içeriğe geç

Python Metaclass’larına Giriş

Bu eğitimde, metaclass’ların ne olduğunu, Python’da nasıl uygulanacağını ve nasıl özelleştirilebileceğini öğrenin.
· 6 dk. oku

Yapay Zeka ile Keşfedin

Python’da bir metaclass, bir sınıfın nasıl davranacağını tanımlayan sınıfın sınıfıdır. Bir sınıfın kendisi bir metaclass örneğidir. Python’da bir sınıf, o sınıfın örneğinin nasıl davranacağını tanımlar. Metaclass’ları iyi anlamak için önceden Python sınıflarıyla çalışma deneyimine sahip olmak gerekir. Metaclass’lara derinlemesine dalmadan önce birkaç kavramı aradan çıkaralım.

Python’da her şey bir nesnedir

class TestClass():
    pass

my_test_class = TestClass()
print(my_test_class)
<__main__.TestClass object at 0x7f6fcc6bf908>

Python sınıfları dinamik olarak oluşturulabilir

Python’daki type, bir nesnenin türünü bulmamızı sağlar. Yukarıda oluşturduğumuz nesnenin türünü kontrol edebiliriz.

type(TestClass)
type
type(type)
type

Bir dakika, ne oldu? Yukarıda oluşturduğumuz nesnenin türünün class olmasını beklerdik, ama değil. Bu düşünceyi aklınızda tutun. Birazdan bunu ele alacağız. Ayrıca type’ın kendi türünün de type olduğunu görüyoruz. O, type’ın bir örneğidir. type’ın yaptığı bir başka sihir de sınıfları dinamik olarak oluşturmamızı sağlamasıdır. Bunu aşağıda nasıl yapacağımızı gösterelim. Aşağıda gösterilen DataCamp sınıfı, type kullanılarak şu şekilde oluşturulabilir:

class DataCamp():
    pass
DataCampClass = type('DataCamp', (), {})
print(DataCampClass)
print(DataCamp())
<class '__main__.DataCamp'>
<__main__.DataCamp object at 0x7f6fcc66e358>

Yukarıdaki örnekte DataCamp sınıf adıyken, DataCampClass sınıfın referansını tutan değişkendir. type kullanırken, aşağıda gösterildiği gibi bir sözlük aracılığıyla sınıfın özniteliklerini iletebiliriz:

PythonClass = type('PythonClass', (), {'start_date': 'August 2018', 'instructor': 'John Doe'} )
print(PythonClass.start_date, PythonClass.instructor)
print(PythonClass)
August 2018 John Doe
<class '__main__.PythonClass'>

PythonClass’ımızın DataCamp sınıfından miras almasını istersek, type ile sınıfı tanımlarken ikinci argümana bunu geçiririz

PythonClass = type('PythonClass', (DataCamp,), {'start_date': 'August 2018', 'instructor': 'John Doe'} )
print(PythonClass)
<class '__main__.PythonClass'>

Artık bu iki kavramı aradan çıkardığımıza göre, Python’un sınıfları bir metaclass kullanarak oluşturduğunu fark ediyoruz. Python’da her şeyin bir nesne olduğunu gördük, bu nesneler metaclass’lar tarafından oluşturulur. Bir sınıf oluşturmak için class dediğimizde, perde arkasında sınıfı oluşturan sihri yapan bir metaclass vardır. Bunu pratikte yukarıda type ile zaten gördük. Bu, string’leri oluşturan str ve tamsayıları oluşturan int gibidir. Python’da ___class__ özniteliği, mevcut örneğin türünü kontrol etmemizi sağlar. Aşağıda bir string oluşturalım ve türünü kontrol edelim.

article = 'metaclasses'
article.__class__
str

type(article) kullanarak da türü kontrol edebiliriz.

type(article)
str

str’ın türünü kontrol ettiğimizde, onun da type olduğunu görürüz.

type(str)
type

float, int, list, tuple ve dict için türü kontrol ettiğimizde benzer bir çıktı alırız. Bunun nedeni, bu nesnelerin tümünün type türünde olmasıdır.

print(type(list),type(float), type(dict), type(tuple))
<class 'type'> <class 'type'> <class 'type'> <class 'type'>

type’ın sınıfları oluşturduğunu zaten gördük. Dolayısıyla __class__’ın __class__’ını kontrol ettiğimizde, bunun type döndürmesi gerekir.

article.__class__.__class__
type

Özel Metaclass Oluşturma

Python’da sınıf oluşturma sürecini, sınıf tanımında metaclass anahtar sözcüğünü geçirerek özelleştirebiliriz. Bu, bu anahtar sözcüğü zaten geçirmiş bir sınıftan miras alınarak da yapılabilir.

class MyMeta(type):
    pass

class MyClass(metaclass=MyMeta):
    pass

class MySubclass(MyClass):
    pass

Aşağıda MyMeta sınıfının türünün type olduğunu ve MyClass ile MySubClass’ın türünün MyMeta olduğunu görebiliriz.

print(type(MyMeta))
print(type(MyClass))
print(type(MySubclass))
<class 'type'>
<class '__main__.MyMeta'>
<class '__main__.MyMeta'>

Bir sınıf tanımlanırken herhangi bir metaclass belirtilmemişse, varsayılan type metaclass’ı kullanılır. Bir metaclass verilmişse ve type()’ın bir örneği değilse, doğrudan metaclass olarak kullanılır.

__new__ ve __init__

Metaclass’lar aşağıda gösterilen iki yoldan biriyle de tanımlanabilir. Aralarındaki farkı aşağıda açıklayacağız.

class MetaOne(type):
    def __new__(cls, name, bases, dict):
        pass

class MetaTwo(type):
    def __init__(self, name, bases, dict):
        pass

__new__, sınıf oluşturulmadan önce dict veya bases demetlerini tanımlamak istediğinizde kullanılır. __new__’ün dönüş değeri genellikle cls’in bir örneğidir. __new__, değiştirilemeyen türlerin alt sınıflarının örnek oluşturmayı özelleştirmesine olanak tanır. Sınıf oluşturmayı özelleştirmek için özel metaclass’larda geçersiz kılınabilir. __init__ ise genellikle nesne oluşturulduktan sonra onu başlatmak için çağrılır.

Metaclass __call__ yöntemi

Resmi belgelere göre, sınıf çağrıldığında özel davranışa izin veren özel bir __call__() yöntemi metaclass’ta tanımlanarak diğer sınıf yöntemleri de geçersiz kılınabilir.

Metaclass __prepare__ yöntemi

Python’un veri modeli belgelerine göre

Uygun metaclass belirlendikten sonra sınıf ad alanı hazırlanır. Metaclass’ın bir __prepare__ özniteliği varsa, sınıf tanımından gelen ek anahtar sözcük argümanlarıyla birlikte namespace = metaclass.__prepare__(name, bases, **kwds) olarak çağrılır. Metaclass’ın bir __prepare__ özniteliği yoksa, sınıf ad alanı boş, sıralı bir eşlem olarak başlatılır. - docs.python.org

Bir Metaclass kullanarak Singleton Tasarımı

Bu, bir sınıfın örneklenmesini yalnızca bir nesneyle sınırlandıran bir tasarım desenidir. Örneğin veritabanına bağlanmak için bir sınıf tasarlarken faydalı olabilir. Bağlantı sınıfının yalnızca tek bir örneğine sahip olmak isteyebilirsiniz.

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(SingletonMeta,cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class SingletonClass(metaclass=SingletonMeta):
    pass

Sonuç

Bu yazıda, metaclass’ların ne olduğunu ve Python programlamamızda onları nasıl uygulayabileceğimizi öğrendik. Metaclass’lar; günlükleme, sınıfların oluşturulma anında kaydedilmesi ve profilleme gibi alanlarda uygulanabilir. Oldukça soyut kavramlar gibi görünüyorlar ve bunlara gerçekten ihtiyacınız olup olmadığını merak ediyor olabilirsiniz. Uzun yıllardır Python kullanan Tim Peters bu soruyu en iyi şekilde yanıtlıyor.

"Metaclass’lar, kullanıcıların %99’unun asla dert etmemesi gereken daha derin büyülerdir. Onlara ihtiyacınız olup olmadığını merak ediyorsanız, yoktur (onlara gerçekten ihtiyaç duyanlar buna kesin olarak emindir ve nedenine dair bir açıklamaya ihtiyaç duymazlar).” - realpython

Python hakkında daha fazla bilgi edinmek isterseniz, DataCamp’in Python Programlama beceri yoluna başlayın ve Python Sınıfları Eğitimimize göz atın.

Kaynaklar

Birinci bağlantı

İkinci bağlantı

Konular

İlgili