Metasınıflar nelerdir ve bunları ne için kullanıyoruz?
Bir metaclass bir sınıfın sınıfıdır. Bir sınıf, bir sınıf örneğinin (örneğin bir nesne) bir metaclass bir sınıfın nasıl davranacağını tanımlarken nasıl davranacağını tanımlar. Bir sınıf bir metaclass örneğidir.
Python'da metaclass'lar için rasgele hileler kullanabiliyorken ( Jerub show gibi), daha iyi yaklaşım onu gerçek bir sınıf yapmaktır. type
, Python'daki olağan metaclass'tır. type
kendisi bir sınıftır ve kendi türüdür. Python'da type
gibi bir şeyi yeniden yaratamazsınız, ancak Python biraz hile yapar. Python'da kendi metaclass'ınızı oluşturmak için gerçekten sadece type
alt sınıfını almak istiyorsunuz.
Bir metaclass en çok sınıf fabrikası olarak kullanılır. Sınıfı çağırarak bir nesne oluşturduğunuzda Python, metaclass'ı çağırarak yeni bir sınıf oluşturur ('class' deyimini çalıştırdığında). Normal __init__
ve __new__
yöntemleriyle birleştirilen metaclass'lar, bir sınıf oluştururken 'yeni şeyler bir kayıt defterine kaydettirmek veya sınıfı tamamen başka bir şeyle değiştirmek gibi' ekstra şeyler yapmanıza izin verir.
class
ifadesi çalıştırıldığında, Python önce class
ifadesinin gövdesini normal bir kod bloğu olarak yürütür. Sonuçta ortaya çıkan ad alanı (bir dikme), bulunacak sınıfın niteliklerini tutar. Meta sınıf, yapılacak olan sınıfın temel sınıflarına (metaclass'ler miras alınır), yapılacak olan sınıfın (eğer varsa) __metaclass__
özniteliğine veya __metaclass__
global değişkenine bakılarak belirlenir. Metaclass daha sonra onu başlatmak için sınıfın adı, temelleri ve nitelikleri ile çağrılır.
Ancak, metasınıflar aslında sadece bir fabrika değil, sınıfın type türünü tanımlar, böylece onlarla daha fazlasını yapabilirsiniz. Örneğin, metaclass'taki normal yöntemleri tanımlayabilirsiniz. Bu metaclass metotları, sınıfsız olarak sınıfta çağrılabilmeleri için sınıf metotlarına benzerler, ancak sınıfın bir sınıfında çağrılmayacakları sınıf metotlarına benzemezler. type.__subclasses__()
, type
metaclass öğesindeki bir yönteme örnektir. Sınıfın çalışma şeklini uygulamak veya değiştirmek için __add__
, __iter__
ve __getattr__
gibi normal 'magic' yöntemlerini de tanımlayabilirsiniz.
İşte parça ve parçaların toplu bir örneği:
def make_hook(f):
"""Decorator to turn 'foo' method into '__foo__'"""
f.is_hook = 1
return f
class MyType(type):
def __new__(mcls, name, bases, attrs):
if name.startswith('None'):
return None
# Go over attributes and see if they should be renamed.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_hook', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Would register class %s now." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Alternatively, to autogenerate the classname as well as the class:
# return type(self.__+ other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "Would unregister class %s now." % self
class MyObject:
__metaclass__ = MyType
class NoneSample(MyObject):
pass
# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)
class Example(MyObject):
def __init__(self, value):
self.value = value
@make_hook
def add(self, other):
return self.__class__(self.value + other.value)
# Will unregister the class
Example.unregister()
inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()
print inst + inst
class Sibling(MyObject):
pass
ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
Metasınıfları anlamadan önce, Python'da ders vermeniz gerekir. Ve Python'un, Smalltalk dilinden ödünç alınan sınıfların ne olduğuna dair çok tuhaf bir fikri vardır.
Çoğu dilde, sınıflar bir nesnenin nasıl üretileceğini tanımlayan sadece kod parçalarıdır. Bu Python'da da doğru:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
Ancak sınıflar Python'da olduğundan daha fazla. Sınıflar da nesnelerdir.
Evet nesneler.
class
anahtar sözcüğünü kullanır kullanmaz, Python bunu çalıştırır ve bir NESNE oluşturur. Talimat
>>> class ObjectCreator(object):
... pass
...
bellekte "ObjectCreator" adında bir nesne oluşturur.
Bu nesne (sınıf) kendisi nesne (örnek) oluşturabilir ve bu nedenle de bir sınıf .
Ama yine de, bu bir nesne ve bu nedenle:
Örneğin.:
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
Sınıflar nesne olduğundan, onları anında, herhangi bir nesne gibi oluşturabilirsiniz.
İlk önce, class
işlevini kullanarak bir işlevde sınıf oluşturabilirsiniz:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
Ama o kadar dinamik değil, çünkü hala tüm sınıfı kendiniz yazmak zorundasınız.
Sınıflar nesne olduğundan, bir şey tarafından üretilmeleri gerekir.
class
anahtar sözcüğünü kullandığınızda, Python bu nesneyi otomatik olarak oluşturur. Ancak Python'daki çoğu şeyde olduğu gibi, bunu manuel olarak yapmanız için bir yol sunar.
type
işlevini hatırlıyor musunuz? Hangi tür bir nesne olduğunu bilmenizi sağlayan eski fonksiyon:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
Eh, type
NAME_ tamamen farklı bir yeteneğe sahip, aynı zamanda anında sınıfları oluşturabilir. type
name__, bir sınıfın tanımını parametre olarak alabilir ve bir sınıf döndürür.
(Biliyorum, aynı işlevin kendinize geçirdiğiniz parametrelere göre iki farklı kullanımı olabilir. Python'daki geriye dönük uyumluluktan dolayı bu bir sorun)
type
şu şekilde çalışır:
type(name of the class,
Tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)
Örneğin.:
>>> class MyShinyClass(object):
... pass
manuel olarak bu şekilde oluşturulabilir:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
Sınıfın referansı olarak "MyShinyClass" ı sınıfın adı ve değişken olarak kullandığımızı fark edeceksiniz. Farklı olabilirler, ancak işleri karmaşıklaştırmak için hiçbir neden yoktur.
type
name__, sınıfın niteliklerini tanımlamak için bir sözlük kabul eder. Yani:
>>> class Foo(object):
... bar = True
Çevrilebilir:
>>> Foo = type('Foo', (), {'bar':True})
Ve normal bir sınıf olarak kullanılır:
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True
Ve elbette, bundan miras alabilirsiniz, yani:
>>> class FooChild(Foo):
... pass
olabilir:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True
Sonunda sınıfınıza yöntemler eklemek istersiniz. Sadece uygun imzayla bir işlev tanımlayın ve onu nitelik olarak atayın.
>>> def echo_bar(self):
... print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True
Sınıfı dinamik olarak oluşturduktan sonra, normalde oluşturulmuş bir sınıf nesnesine yöntemler eklemek gibi, daha da fazla yöntem ekleyebilirsiniz.
>>> def echo_bar_more(self):
... print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True
Nereye gittiğimizi görüyorsunuz: Python'da sınıflar nesnedir ve dinamik olarak, anında bir sınıf oluşturabilirsiniz.
Python, class
anahtar sözcüğünü kullandığınızda yaptığınız şeydir ve bir metaclass kullanarak bunu yapar.
Meta gözlükler, sınıfları oluşturan 'eşyalardır.
Nesneleri oluşturmak için sınıfları tanımlarsın, değil mi?
Ancak Python sınıflarının nesne olduğunu öğrendik.
Metaklas, bu nesneleri yaratan şeydir. Onlar sınıfların sınıflarıdır, onları şu şekilde hayal edebilirsiniz:
MyClass = MetaClass()
my_object = MyClass()
type
öğesinin şöyle bir şey yapmanıza izin verdiğini gördünüz:
MyClass = type('MyClass', (), {})
Bunun nedeni type
işlevinin aslında bir metaclass olmasıdır. type
name__, Python'un sahne arkasındaki tüm sınıfları oluşturmak için kullandığı metaclass'tır.
Şimdi neden haltın küçük harfle yazıldığını merak ediyorsunuz ve Type
değil mi?
Eh, sanırım bu, dizeleri nesneler oluşturan sınıf str
ve tamsayı nesneleri oluşturan sınıf int
ile tutarlılık meselesi. type
name__, yalnızca sınıf nesnelerini oluşturan sınıftır.
Bunu __class__
niteliğini kontrol ederek görürsünüz.
Her şey ve her şeyi kastediyorum, Python'da bir nesnedir. Bu, ints, stringler, fonksiyonlar ve sınıfları içerir. Hepsi nesnelerdir. Ve hepsi bir sınıftan yaratıldı:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
Şimdi, herhangi bir __class__
__class__
nedir?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
Yani, bir metaclass sadece sınıf nesnelerini yaratan şeydir.
İsterseniz buna 'sınıf fabrikası' diyebilirsiniz.
type
name__, Python'un kullandığı yerleşik metaclass türüdür, ancak elbette, kendi metaclass'ınızı oluşturabilirsiniz.
__metaclass__
özelliğiPython 2'de, bir sınıf yazarken bir __metaclass__
niteliği ekleyebilirsiniz (Python 3 sözdizimi için bir sonraki bölüme bakın):
class Foo(object):
__metaclass__ = something...
[...]
Bunu yaparsanız, Python metaclass'ı Foo
sınıfını oluşturmak için kullanır.
Dikkat et, çok zor.
Önce class Foo(object)
yazıyorsunuz, fakat sınıf nesnesi Foo
henüz bellekte oluşturulmadı.
Python, sınıf tanımında __metaclass__
dosyasını arayacaktır. Onu bulursa, nesne sınıfı Foo
oluşturmak için kullanır. Olmazsa, sınıfı oluşturmak için type
işlevini kullanır.
Bunu birkaç kez oku.
Ne zaman yaparsın:
class Foo(Bar):
pass
Python aşağıdakileri yapar:
Foo
içinde __metaclass__
niteliği var mı?
Eğer öyleyse, bellekte bir sınıf nesnesi oluşturun (bir sınıf nesnesi dedim, burada benimle kal), Foo
adıyla __metaclass__
içinde olanı kullanarak.
Python __metaclass__
'u bulamazsa, MODULE seviyesinde bir __metaclass__
arayacak ve aynı şeyi yapmaya çalışacaktır (sadece bir şeyi miras almamış, temelde eski stil sınıfları için).
Sonra, eğer herhangi bir __metaclass__
bulamazsa, sınıf nesnesini oluşturmak için Bar
name __ 'ın (ilk ebeveyn) kendi metaclass'ını (varsayılan type
olabilir) kullanır.
Burada, __metaclass__
özniteliğinin miras alınmayacağına dikkat edin, üst öğenin metaclass'ı (Bar.__class__
) olacaktır. Bar
name__, Bar
ile type()
(ve type.__new__()
değil) oluşturan bir __metaclass__
niteliği kullanırsa, alt sınıflar bu davranışı devralmaz.
Şimdi büyük soru şudur: __metaclass__
içine ne koyabilirsiniz?
Cevap: bir sınıf oluşturabilecek bir şey.
Ve bir sınıf ne yaratabilir? type
veya alt sınıfı olan veya kullanan herhangi bir şey.
Metaclass'ı ayarlamak için kullanılan sözdizimi Python 3'te değiştirildi:
class Foo(object, metaclass=something):
...
yani, __metaclass__
niteliği artık, temel sınıflar listesindeki bir anahtar kelime argümanı lehine artık kullanılmamaktadır.
Ancak metasınıfların davranışı büyük ölçüde aynı kalır .
Python 3'teki metaclass'lara eklenen bir şey, anahtar sözcük argümanları gibi nitelikleri bir metaclass'a da aktarabilirsiniz:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
Python'un bunu nasıl ele aldığına ilişkin aşağıdaki bölümü okuyun.
Bir metaclass'ın asıl amacı, oluşturulduğunda sınıfı otomatik olarak değiştirmektir.
Bunu genellikle mevcut bağlamla eşleşen sınıflar oluşturmak istediğiniz API'ler için yaparsınız.
Modülünüzdeki tüm sınıfların niteliklerini büyük harfle yazması gerektiğine karar verdiğiniz aptal bir örnek düşünün. Bunu yapmanın birkaç yolu vardır, ancak bunun bir yolu __metaclass__
modül düzeyine ayarlamaktır.
Bu şekilde, bu modülün tüm sınıfları bu metaclass kullanılarak oluşturulacak ve metaclass'a tüm özellikleri büyük harfe dönüştürmesini söylemeliyiz.
Neyse ki, __metaclass__
aslında herhangi bir çağrılabilir olabilir, resmi bir sınıf olması gerekmez (Biliyorum, adında 'sınıf' olan bir şeyin bir sınıf olması gerekmiyor, figür figürü olmalı ... ama yardımcı olur).
Böylece bir fonksiyon kullanarak basit bir örnekle başlayacağız.
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True
f = Foo()
print(f.BAR)
# Out: 'bip'
Şimdi, aynısını yapalım, fakat bir metaclass için gerçek bir sınıf kullanalım:
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)
Ancak bu gerçekten OOP değil. type
işlevini doğrudan çağırırız ve üst __new__
üst bilgisini geçersiz kılmaz veya çağırmayız. Haydi Yapalım şunu:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)
upperattr_metaclass
ek argümanını fark etmiş olabilirsiniz. Bu konuda özel bir şey yok: __new__
her zaman içinde tanımlandığı sınıfı, ilk parametre olarak alır. Tıpkı, örneği ilk parametre olarak alan sıradan yöntemler veya sınıf yöntemleri için tanımlayıcı sınıf için self
öğeniz gibi.
Elbette, burada kullandığım isimler açıklık adına uzun, ama self
için olduğu gibi, tüm argümanların geleneksel isimleri var. Böylece gerçek bir üretim metaclass şöyle görünür:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type.__new__(cls, clsname, bases, uppercase_attr)
Mirasını kolaylaştıracak super
kullanarak daha temiz hale getirebiliriz (çünkü evet, metalden miras alan, türden miras alan metasınıflara sahip olabilirsiniz):
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)
Oh, ve eğer python 3'te bu çağrıyı, anahtar sözcük argümanları ile yaparsanız, şöyle:
class Foo(object, metaclass=Thing, kwarg1=value1):
...
Bunu kullanmak için metaclass içindeki çevirir:
class Thing(type):
def __new__(cls, clsname, bases, dct, kwargs1=default):
...
Bu kadar. Metaclass hakkında daha fazla bir şey yok.
Metalllass kullanan kodun karmaşıklığının arkasındaki nedeni metaclasslar değil, introspeksiyona dayanan, kalıtım manipüle eden, __dict__
, vb.
Gerçekten de, metasınıflar özellikle kara büyü yapmakta ve dolayısıyla karmaşık şeyleri yapmakta kullanışlıdır. Ancak kendi başlarına, basittirler:
__metaclass__
herhangi bir çağrıyı kabul edebildiğinden, açıkça daha karmaşık olduğu için neden bir sınıf kullandınız?
Bunu yapmak için birkaç neden var:
UpperAttrMetaclass(type)
dosyasını okuduğunuzda neyin takip edeceğini bilirsiniz__new__
, __init__
ve __call__
öğelerine bağlanabilirsiniz. Farklı şeyler yapmana izin verecek. Her şeyi __new__
ile yapabiliyor olsanız bile, bazı insanlar __init__
işlevini kullanmaktan daha rahat.Şimdi büyük soru. Neden bazı gizli hatalara eğilimli bir özellik kullanıyorsunuz?
Genelde yapmazsın:
Meta gözlükler, kullanıcıların% 99'unun asla endişelenmemesi gereken daha derin bir sihirdir. Onlara ihtiyaç duyup duymadığınızı merak ediyorsanız, (gerçekten ihtiyacı olan insanlar kendilerine ihtiyaç duyduklarını kesin olarak bilirler ve nedenleriyle ilgili bir açıklama yapmaları gerekmez).
Python Guru Tim Peters
Bir metaclass için ana kullanım durumu bir API oluşturmaktır. Bunun tipik bir örneği Django ORM'dir.
Böyle bir şeyi tanımlamanıza izin verir:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
Ama bunu yaparsan:
guy = Person(name='bob', age='35')
print(guy.age)
Bir IntegerField
nesnesini döndürmez. Bir int
döndürür ve doğrudan veritabanından bile alabilir.
Bu mümkündür çünkü models.Model
__metaclass__
'yi tanımlar ve basit ifadelerle tanımladığınız Person
işlevini bir veritabanı alanına karmaşık bir kanca haline getirecek bir sihir kullanır.
Django, basit bir API'yi açığa çıkararak ve metasınıfları kullanarak, sahnelerin arkasındaki asıl işi yapmak için bu API'den kodu yeniden oluşturarak karmaşık bir şeyi basitleştirir.
İlk önce, sınıfların örnek oluşturabilecek nesneler olduğunu biliyorsunuz.
Aslında, sınıfların kendileri birer örnek. Metaclasses.
>>> class Foo(object): pass
>>> id(Foo)
142630324
Her şey Python'da bir nesnedir ve hepsi sınıf örnekleri veya metaclass örnekleridir.
type
dışında.
type
aslında kendi metaclass'ýdýr. Bu, saf Python ile yeniden üretebileceğiniz bir şey değildir ve uygulama düzeyinde biraz hile yaparak yapılır.
İkincisi, metasınıflar karmaşıktır. Bunları çok basit sınıf değişiklikleri için kullanmak istemeyebilirsiniz. İki farklı teknik kullanarak sınıfları değiştirebilirsiniz:
Sınıf değişikliğine ihtiyaç duyduğunuz zamanın% 99'unu, bunları kullanmanız daha iyi olur.
Ancak, zamanın% 98'i, hiçbir şekilde sınıf değişikliği yapmanıza gerek yoktur.
Not, bu cevap Python 2.x için, 2008'de yazıldığı gibi, metasınıflar 3.x'te biraz farklıdır.
Metaclasses 'sınıf' çalışmasını sağlayan gizli sos. Yeni bir stil nesnesi için varsayılan metaclass 'tip' olarak adlandırılır.
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type
Metaclasses 3 args alır. 'isim', 'taban' ve 'dikt'
İşte sırrın başladığı yer. Bu örnek sınıf tanımında adın, temellerin ve dize nereden geldiğine bakın.
class ThisIsTheName(Bases, Are, Here):
All_the_code_here
def doesIs(create, a):
dict
'Class:' in nasıl çağırdığını gösterecek bir metaclass tanımlayalım.
def test_metaclass(name, bases, dict):
print 'The Class Name is', name
print 'The Class Bases are', bases
print 'The dict has', len(dict), 'elems, the keys are', dict.keys()
return "yellow"
class TestName(object, None, int, 1):
__metaclass__ = test_metaclass
foo = 1
def baz(self, arr):
pass
print 'TestName = ', repr(TestName)
# output =>
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName = 'yellow'
Ve şimdi, aslında bir anlam ifade eden bir örnek, bu, değişkenleri sınıfta ayarlanan ve “Yok” olarak ayarlanan “öznitelikler” listesinde otomatik olarak yapacaktır.
def init_attributes(name, bases, dict):
if 'attributes' in dict:
for attr in dict['attributes']:
dict[attr] = None
return type(name, bases, dict)
class Initialised(object):
__metaclass__ = init_attributes
attributes = ['foo', 'bar', 'baz']
print 'foo =>', Initialised.foo
# output=>
foo => None
'Initalised' özelliğinin init_attributes
metaclass değerini alarak kazandığı sihirli davranışın Initalised alt sınıfına geçmediğine dikkat edin.
Sınıf oluşturulduğunda nasıl bir eylem gerçekleştiren bir metaclass yapmak için 'type' alt sınıfını gösterebileceğinizi gösteren daha somut bir örnek. Bu oldukça zor:
class MetaSingleton(type):
instance = None
def __call__(cls, *args, **kw):
if cls.instance is None:
cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
return cls.instance
class Foo(object):
__metaclass__ = MetaSingleton
a = Foo()
b = Foo()
assert a is b
Metaclasses için bir kullanım otomatik olarak bir örneğe yeni özellikler ve yöntemler eklemektir.
Örneğin, Django models 'e bakarsanız, tanımları kafa karıştırıcı görünüyor. Yalnızca sınıf özelliklerini tanımlıyormuşsunuz gibi görünüyor:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Bununla birlikte, çalışma zamanında Person nesneleri her türlü faydalı yöntemle doldurulur. Bazı şaşırtıcı metaclassery için source 'a bakın.
Diğerleri metasınıfların nasıl çalıştığını ve Python tipi sisteme nasıl uyduğunu açıkladılar. İşte ne için kullanılabileceklerine bir örnek. Yazdığım bir test çerçevesinde, sınıfların tanımlandığı sırayı takip etmek istedim, böylece onları daha sonra bu sıraya göre başlatabilirdim. Bir metaclass kullanarak bunu yapmak en kolay buldum.
class MyMeta(type):
counter = 0
def __init__(cls, name, bases, dic):
type.__init__(cls, name, bases, dic)
cls._order = MyMeta.counter
MyMeta.counter += 1
class MyType(object): # Python 2
__metaclass__ = MyMeta
class MyType(metaclass=MyMeta): # Python 3
pass
MyType
öğesinin bir alt sınıfı olan herhangi bir şey, daha sonra sınıfların tanımlandığı sırayı kaydeden _order
sınıf özniteliğini alır.
ONLamp'ın metaclass programlamaya girişinin iyi yazılmış olduğunu düşünüyorum ve zaten birkaç yaşında olmasına rağmen konuya gerçekten iyi bir giriş yaptığını düşünüyorum.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html ( https://web.archive.org/web/20080206005253 tarihinde arşivlendi) /http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html )
Kısacası: Bir sınıf, örneğin oluşturulması için bir taslaktır, bir metaclass, bir sınıf oluşturulması için bir taslaktır. Python sınıflarında bu davranışı etkinleştirmek için birinci sınıf nesneler olması gerektiği kolayca görülebilir.
Hiç kendimden bir tane yazmadım, ama bence metasınıfların en güzel kullanımlarından biri Django framework içinde görülebilir. Model sınıfları, yeni modeller veya form sınıfları yazmak için bildirimsel bir stil sağlamak için bir metaclass yaklaşımı kullanır. Metaclass sınıfı yaratırken, tüm üyeler sınıfın kendisini kişiselleştirme olanağına sahip olur.
Söylenecek şey şudur: Eğer metasınıfların ne olduğunu bilmiyorsanız, sizin onlara ihtiyaç duymama ihtimaliniz % 99'dur.
Python 3 güncellemesi
Bir metaclass'ta (bu noktada) iki anahtar yöntem vardır:
__prepare__
ve__new__
__prepare__
, sınıf oluşturulurken ad alanı olarak kullanılmak üzere özel bir eşleme (OrderedDict
gibi) sağlar. Seçtiğiniz herhangi bir ad alanının bir örneğini döndürmelisiniz. __prepare__
uygulamıyorsanız normal bir dict
kullanılır.
__new__
, final sınıfının gerçek yaratılmasından/değiştirilmesinden sorumludur.
Çıplak kemikler, hiçbir şey yapmadan ekstra bir metaclass istiyorum:
class Meta(type):
def __prepare__(metaclass, cls, bases):
return dict()
def __new__(metacls, cls, bases, clsdict):
return super().__new__(metacls, cls, bases, clsdict)
Basit bir örnek:
Bazı basit doğrulama kodunun özellikleriniz üzerinde çalışmasını istediğinizi varsayalım - her zaman bir int
veya str
olmalı. Bir metaclass olmadan, sınıfınız şunun gibi görünür:
class Person:
weight = ValidateType('weight', int)
age = ValidateType('age', int)
name = ValidateType('name', str)
Görebildiğiniz gibi, özniteliğin adını iki kez tekrarlamanız gerekir. Bu tahriş edici böcek ile birlikte yazım hataları mümkün kılar.
Basit bir metaclass bu sorunu çözebilir:
class Person(metaclass=Validator):
weight = ValidateType(int)
age = ValidateType(int)
name = ValidateType(str)
Metaclass'ın neye benzeyeceği budur (gerekli olmadığından __prepare__
kullanmıyorsunuz):
class Validator(type):
def __new__(metacls, cls, bases, clsdict):
# search clsdict looking for ValidateType descriptors
for name, attr in clsdict.items():
if isinstance(attr, ValidateType):
attr.name = name
attr.attr = '_' + name
# create final class and return it
return super().__new__(metacls, cls, bases, clsdict)
Bir örnek çalışma:
p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'
üretir:
9
Traceback (most recent call last):
File "simple_meta.py", line 36, in <module>
p.weight = '9'
File "simple_meta.py", line 24, in __set__
(self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')
Not : Bu örnek, bir sınıf dekoratörüyle de yapılabilecek kadar basit, ancak muhtemelen gerçek bir metaclass daha fazlasını yapıyor olabilir.
Başvuru için 'ValidateType' sınıfı:
class ValidateType:
def __init__(self, type):
self.name = None # will be set by metaclass
self.attr = None # will be set by metaclass
self.type = type
def __get__(self, inst, cls):
if inst is None:
return self
else:
return inst.__dict__[self.attr]
def __set__(self, inst, value):
if not isinstance(value, self.type):
raise TypeError('%s must be of type(s) %s (got %r)' %
(self.name, self.type, value))
else:
inst.__dict__[self.attr] = value
__call__()
yönteminin rolüPython programlamayı birkaç aydan daha uzun bir süre boyunca yaptıysanız, sonunda şuna benzer bir kod yazmanız gerekir:
# define a class
class SomeClass(object):
# ...
# some definition here ...
# ...
# create an instance of it
instance = SomeClass()
# then call the object as if it's a function
result = instance('foo', 'bar')
Sonuncusu, sınıfta __call__()
magic metodunu uyguladığınızda mümkündür.
class SomeClass(object):
# ...
# some definition here ...
# ...
def __call__(self, foo, bar):
return bar + foo
Bir sınıfın bir örneği çağrılabilir olarak kullanıldığında __call__()
yöntemi çağrılır. Ancak, önceki cevaplardan gördüğümüz gibi, sınıfın kendisi bir metaclass örneğidir, bu yüzden sınıfı çağrılabilir olarak kullandığımızda (yani, bir örneğini oluşturduğumuzda) aslında metaclass '__call__()
yöntemini çağırıyoruz. Bu noktada çoğu Python programcısının kafası biraz karışık çünkü bu instance = SomeClass()
gibi bir örnek oluştururken __init__()
yöntemini çağırdığınız söylendi. Daha derine inen bazı kişiler __init__()
'den önce __new__()
olduğunu biliyor. Bugün, başka bir doğruluk katmanı ortaya çıkıyor, __new__()
önce metaclass '__call__()
.
Yöntem çağrı zincirini, özellikle bir sınıf örneği oluşturma perspektifinden inceleyelim.
Bu, bir örnek oluşturulmadan önceki anı ve geri döndürmek üzere olduğu anı kaydeden bir metaclass.
class Meta_1(type):
def __call__(cls):
print "Meta_1.__call__() before creating an instance of ", cls
instance = super(Meta_1, cls).__call__()
print "Meta_1.__call__() about to return instance."
return instance
Bu, bu metaclass'ı kullanan bir sınıftır
class Class_1(object):
__metaclass__ = Meta_1
def __new__(cls):
print "Class_1.__new__() before creating an instance."
instance = super(Class_1, cls).__new__(cls)
print "Class_1.__new__() about to return instance."
return instance
def __init__(self):
print "entering Class_1.__init__() for instance initialization."
super(Class_1,self).__init__()
print "exiting Class_1.__init__()."
Şimdi bir Class_1
örneği oluşturalım.
instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.
Yukarıdaki kodun, görevleri kaydetmekten başka bir şey yapmadığını gözlemleyin. Her yöntem, asıl işi ebeveyni için görevlendirerek varsayılan davranışı korur. type
Meta_1
'nin ebeveyn sınıfı olduğundan (type
, varsayılan ana metaclass'tır) ve yukarıdaki çıktının sipariş sırasını göz önünde bulundurarak, şimdi type.__call__()
'nin sözde uygulamasının ne olacağına dair bir ipucumuz var:
class type:
def __call__(cls, *args, **kwarg):
# ... maybe a few things done to cls here
# then we call __new__() on the class to create an instance
instance = cls.__new__(cls, *args, **kwargs)
# ... maybe a few things done to the instance here
# then we initialize the instance with its __init__() method
instance.__init__(*args, **kwargs)
# ... maybe a few more things done to instance here
# then we return it
return instance
Metaclass '__call__()
yönteminin ilk adı verilen yöntem olduğunu görebiliriz. Daha sonra, örneğin oluşturulmasını sınıfın __new__()
yöntemine ve örneğin __init__()
işlevinin ilk durumuna getirme yetkisi verir. Aynı zamanda nihayetinde örneği geri veren de o.
Yukarıdan itibaren, metaclass '__call__()
' a ayrıca Class_1.__new__()
ya da Class_1.__init__()
çağrısının yapılıp yapılmayacağına karar verme imkanı verilir. Uygulaması boyunca bu yöntemlerden herhangi biriyle dokunulmamış bir nesneyi geri getirebilirdi. Örneğin, bu yaklaşımı singleton modeline uygulayın:
class Meta_2(type):
singletons = {}
def __call__(cls, *args, **kwargs):
if cls in Meta_2.singletons:
# we return the only instance and skip a call to __new__()
# and __init__()
print ("{} singleton returning from Meta_2.__call__(), "
"skipping creation of new instance.".format(cls))
return Meta_2.singletons[cls]
# else if the singleton isn't present we proceed as usual
print "Meta_2.__call__() before creating an instance."
instance = super(Meta_2, cls).__call__(*args, **kwargs)
Meta_2.singletons[cls] = instance
print "Meta_2.__call__() returning new instance."
return instance
class Class_2(object):
__metaclass__ = Meta_2
def __new__(cls, *args, **kwargs):
print "Class_2.__new__() before creating instance."
instance = super(Class_2, cls).__new__(cls)
print "Class_2.__new__() returning instance."
return instance
def __init__(self, *args, **kwargs):
print "entering Class_2.__init__() for initialization."
super(Class_2, self).__init__()
print "exiting Class_2.__init__()."
Class_2
türünde bir nesne oluşturmaya çalışırken art arda ne olduğunu görelim.
a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.
b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.
a is b is c # True
Bir metaclass, başka bir sınıfın nasıl (bazı) oluşturulması gerektiğini söyleyen bir sınıftır.
Bu, metaclass'ı problemime bir çözüm olarak gördüğümde: Gerçekten karmaşık bir problemim vardı, muhtemelen farklı şekilde çözülebilirdi, ancak bir metaclass kullanarak çözmeyi seçtim. Karmaşıklık nedeniyle, modüldeki yorumların yazılan kod miktarını aştığı yerlerde yazdığım az sayıdaki modülden biri. İşte burada...
#!/usr/bin/env python
# Copyright (C) 2013-2014 Craig Phillips. All rights reserved.
# This requires some explaining. The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried. I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to. See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType. This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient. The complicated bit
# comes from requiring the GsyncOptions class to be static. By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace. Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet. The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method. This is the first and only time the class will actually have its
# dictionary statically populated. The docopt module is invoked to parse the
# usage document and generate command line options from it. These are then
# paired with their defaults and what's in sys.argv. After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored. This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times. The __getattr__ call hides this by default, returning the
# last item in a property's list. However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
class GsyncListOptions(object):
__initialised = False
class GsyncOptionsType(type):
def __initialiseClass(cls):
if GsyncListOptions._GsyncListOptions__initialised: return
from docopt import docopt
from libgsync.options import doc
from libgsync import __version__
options = docopt(
doc.__doc__ % __version__,
version = __version__,
options_first = True
)
paths = options.pop('<path>', None)
setattr(cls, "destination_path", paths.pop() if paths else None)
setattr(cls, "source_paths", paths)
setattr(cls, "options", options)
for k, v in options.iteritems():
setattr(cls, k, v)
GsyncListOptions._GsyncListOptions__initialised = True
def list(cls):
return GsyncListOptions
def __getattr__(cls, name):
cls.__initialiseClass()
return getattr(GsyncListOptions, name)[-1]
def __setattr__(cls, name, value):
# Substitut option names: --an-option-name for an_option_name
import re
name = re.sub(r'^__', "", re.sub(r'-', "_", name))
listvalue = []
# Ensure value is converted to a list type for GsyncListOptions
if isinstance(value, list):
if value:
listvalue = [] + value
else:
listvalue = [ None ]
else:
listvalue = [ value ]
type.__setattr__(GsyncListOptions, name, listvalue)
# Cleanup this module to prevent tinkering.
import sys
module = sys.modules[__name__]
del module.__dict__['GetGsyncOptionsType']
return GsyncOptionsType
# Our singlton abstract proxy class.
class GsyncOptions(object):
__metaclass__ = GetGsyncOptionsType()
type
aslında bir metaclass
'dır - başka sınıflar oluşturan bir sınıf. metaclass
öğelerinin çoğu, type
öğesinin alt sınıflarıdır. metaclass
, new
sınıfını ilk argümanı olarak alır ve sınıf nesnesine aşağıda belirtilen ayrıntılarla erişim sağlar:
>>> class MetaClass(type):
... def __init__(cls, name, bases, attrs):
... print ('class name: %s' %name )
... print ('Defining class %s' %cls)
... print('Bases %s: ' %bases)
... print('Attributes')
... for (name, value) in attrs.items():
... print ('%s :%r' %(name, value))
...
>>> class NewClass(object, metaclass=MetaClass):
... get_choch='dairy'
...
class name: NewClass
Bases <class 'object'>:
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qual:'NewClass'
Note:
Sınıfın hiçbir zaman somutlaştırılmadığına dikkat edin; sınıfı yaratma eylemi, metaclass
öğesinin yürütülmesini tetikledi.
type(obj)
işlevi size bir nesnenin türünü verir.
Bir sınıfın type()
değeri metaclass şeklindedir.
Bir metaclass kullanmak için:
class Foo(object):
__metaclass__ = MyMetaClass
Python sınıfları kendileridir - örneğin meta sınıflarının nesneleridir.
Sınıfları şu şekilde belirlerken uygulanan varsayılan metaclass:
class foo:
...
meta sınıfı, bazı kuralları tüm sınıflara uygulamak için kullanılır. Örneğin, bir veritabanına erişmek için bir ORM oluşturduğunuzu ve her tablonun kayıtlarının o tabla ile eşlenen bir sınıfta (alanlar, iş kuralları vb. Temel alınarak) metaclass'ın olası kullanımını istediğinizi varsayalım. örneğin, tüm tablolardaki tüm kayıt sınıfları tarafından paylaşılan bağlantı havuzu mantığı. Diğer bir kullanım, birden fazla kayıt sınıfını içeren yabancı anahtarları desteklemek için mantıktır.
metaclass'ı tanımladığınızda, alt sınıfı yazarsınız ve mantığınızı eklemek için aşağıdaki sihirli yöntemleri geçersiz kılabilirsiniz.
class somemeta(type):
__new__(mcs, name, bases, clsdict):
"""
mcs: is the base metaclass, in this case type.
name: name of the new class, as provided by the user.
bases: Tuple of base classes
clsdict: a dictionary containing all methods and attributes defined on class
you must return a class object by invoking the __new__ constructor on the base metaclass.
ie:
return type.__call__(mcs, name, bases, clsdict).
in the following case:
class foo(baseclass):
__metaclass__ = somemeta
an_attr = 12
def bar(self):
...
@classmethod
def foo(cls):
...
arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}
you can modify any of these values before passing on to type
"""
return type.__call__(mcs, name, bases, clsdict)
def __init__(self, name, bases, clsdict):
"""
called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
"""
pass
def __prepare__():
"""
returns a dict or something that can be used as a namespace.
the type will then attach methods and attributes from class definition to it.
call order :
somemeta.__new__ -> type.__new__ -> type.__init__ -> somemeta.__init__
"""
return dict()
def mymethod(cls):
""" works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
"""
pass
her neyse, bu ikisi en sık kullanılan kancalardır. metaclassing güçlüdür ve yukarıda metaclassing kullanımlarının hiçbir yerinde ve ayrıntılı bir listesi yoktur.
Type () işlevi bir nesnenin türünü döndürür veya yeni bir tür oluşturur,
örneğin, type () işleviyle bir Hi sınıfı oluşturabiliriz ve bu şekilde Hi (object) sınıfıyla kullanmaya gerek yoktur:
def func(self, name='mike'):
print('Hi, %s.' % name)
Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.
type(Hi)
type
type(h)
__main__.Hi
Dinamik olarak sınıflar oluşturmak için type () kullanmaya ek olarak, sınıf oluşturma davranışını kontrol edebilir ve metaclass kullanabilirsiniz.
Python nesne modeline göre, sınıf nesnedir, bu yüzden sınıf başka bir sınıfın örneği olmalıdır. Varsayılan olarak, bir Python sınıfı, type sınıfının bir örneğidir. Diğer bir deyişle tür, yerleşik sınıfların çoğunun metaclass'ı ve kullanıcı tanımlı sınıfların metaclass'larıdır.
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class CustomList(list, metaclass=ListMetaclass):
pass
lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')
lst
['custom_list_1', 'custom_list_2']
Metaclass'ta anahtar kelime argümanlarını ilettiğimizde Magic etkili olacak, Python yorumlayıcısını ListMetaclass aracılığıyla CustomList oluşturmak için gösterir. new (), bu noktada, örneğin sınıf tanımını değiştirebilir, yeni bir yöntem ekleyebilir ve sonra değiştirilmiş tanımı geri döndürebiliriz.
Yayınlanan cevaplara ek olarak, bir metaclass
öğesinin bir sınıfın davranışını tanımladığını söyleyebilirim. Böylece, açıkça metaclass'ınızı ayarlayabilirsiniz. Python, class
anahtar sözcüğünü aldığında metaclass
öğesini aramaya başlar. Bulunmazsa - varsayılan metaclass tipi sınıfın nesnesini oluşturmak için kullanılır. __metaclass__
niteliğini kullanarak, sınıfınızın metaclass
öğesini ayarlayabilirsiniz:
class MyClass:
__metaclass__ = type
# write here other method
# write here one more method
print(MyClass.__metaclass__)
Bu şekilde çıktı üretecek:
class 'type'
Ve tabii ki, kendi sınıfınızı kullanarak yaratılan herhangi bir sınıfın davranışını tanımlamak için kendi metaclass
dosyasını oluşturabilirsiniz.
Bunu yapmak için, varsayılan metaclass
tipi sınıfınız, ana metaclass
olduğundan devralınmalıdır:
class MyMetaClass(type):
__metaclass__ = type
# you can write here any behaviour you want
class MyTestClass:
__metaclass__ = MyMetaClass
Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)
Çıktı:
class '__main__.MyMetaClass'
class 'type'