it-swarm-tr.com

Python'da metasınıflar nelerdir?

Metasınıflar nelerdir ve bunları ne için kullanıyoruz?

5115
e-satis

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__
2456
Thomas Wouters

Nesne olarak sınıflar

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.

classanahtar 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:

  • bir değişkene atayabilirsiniz
  • kopyalayabilirsin
  • ona özellikler ekleyebilirsiniz
  • bir işlev parametresi olarak iletebilirsiniz

Ö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>

Dinamik olarak sınıflar oluşturma

Sınıflar nesne olduğundan, onları anında, herhangi bir nesne gibi oluşturabilirsiniz.

İlk önce, classiş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.

classanahtar 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.

typeiş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, typeNAME_ tamamen farklı bir yeteneğe sahip, aynı zamanda anında sınıfları oluşturabilir. typename__, 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.

typename__, 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, classanahtar sözcüğünü kullandığınızda yaptığınız şeydir ve bir metaclass kullanarak bunu yapar.

Metaclass nedir (nihayet)

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 typeişlevinin aslında bir metaclass olmasıdır. typename__, 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 Typedeğil mi?

Eh, sanırım bu, dizeleri nesneler oluşturan sınıf strve tamsayı nesneleri oluşturan sınıf intile tutarlılık meselesi. typename__, 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.

typename__, Python'un kullandığı yerleşik metaclass türüdür, ancak elbette, kendi metaclass'ınızı oluşturabilirsiniz.

__metaclass__ özelliği

Python 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'ı Foosınıfını oluşturmak için kullanır.

Dikkat et, çok zor.

Önce class Foo(object) yazıyorsunuz, fakat sınıf nesnesi Foohenüz bellekte oluşturulmadı.

Python, sınıf tanımında __metaclass__ dosyasını arayacaktır. Onu bulursa, nesne sınıfı Foooluşturmak için kullanır. Olmazsa, sınıfı oluşturmak için typeişlevini kullanır.

Bunu birkaç kez oku.

Ne zaman yaparsın:

class Foo(Bar):
    pass

Python aşağıdakileri yapar:

Fooiç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), Fooadı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 Barname __ 'ın (ilk ebeveyn) kendi metaclass'ını (varsayılan typeolabilir) kullanır.

Burada, __metaclass__ özniteliğinin miras alınmayacağına dikkat edin, üst öğenin metaclass'ı (Bar.__class__) olacaktır. Barname__, Barile 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? typeveya alt sınıfı olan veya kullanan herhangi bir şey.

Metal gözlükler in Python 3

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.

Özel metaclasses

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. typeiş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 selfiç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 superkullanarak 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:

  • sınıf oluşumunu engellemek
  • sınıfı değiştir
  • değiştirilmiş sınıfı döndür

Neden işlev yerine meta sınıfları kullanıyorsunuz?

__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:

  • Amaç açıktır. UpperAttrMetaclass(type) dosyasını okuduğunuzda neyin takip edeceğini bilirsiniz
  • OOP'yi kullanabilirsiniz. Metaclass, metaclass öğesini devralabilir, üst yöntemleri geçersiz kılabilir. Metasınıflar metasınıfları bile kullanabilir.
  • Bir sınıfın alt sınıfları, bir metaclass sınıfı belirtirseniz, ancak bir metaclass işleviyle belirtilmemişse, metaclass örnekleridir.
  • Kodunuzu daha iyi yapılandırabilirsiniz. Metaclass'ları asla yukarıdaki örnekteki gibi önemsiz şeyler için kullanmazsınız. Genellikle karmaşık bir şey için. Birkaç yöntemi yapma ve bunları bir sınıfta gruplama yeteneğine sahip olmak, kodu okumayı kolaylaştırmak için çok yararlıdır.
  • __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.
  • Bunlara metaclass denir, kahretsin! Bir anlamı olmalı!

Neden metasınıfları kullandın?

Ş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 IntegerFieldnesnesini döndürmez. Bir intdö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 Personiş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.

Son kelime

İ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.

typedışında.

typeaslı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.

6226
e-satis

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
347
Jerub

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.

141
Antti Rasinen

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.

140
kindall

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.

104

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
66
Ethan Furman

Sınıf örneği oluştururken bir metaclass '__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. typeMeta_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
57
Michael Ekoka

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()
49
Craig

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.

36
Mushahid Khan

Tl; dr sürümü

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
32

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.

21
Xingzhou Liu

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.

17
binbjz

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, classanahtar 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 metaclassdosyasını oluşturabilirsiniz.

Bunu yapmak için, varsayılan metaclasstipi sınıfınız, ana metaclassolduğ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'
7
ARGeo