15 Soru: Python'da metasınıflar nelerdir?

tarafından oluşturulan soru Thu, Jan 11, 2018 12:00 AM

Metasınıflar nedir ve bunları ne için kullanırız?

    
5184
15 Yanıtlar                              15                         

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. Sınıf, bir metaclass örneğidir.

Python'da, metaclass'lar için isteğe bağlı mermiler kullanabilirsiniz (örneğin, Jerub gösterir), daha iyi yaklaşım onu ​​gerçek bir sınıfın kendisi 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 type alt sınıfını kullanmak 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ştirildiğinde, metasınıflar, 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 yürütüldüğünde, Python ilk ö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. Metaclass, var olan sınıfın temel sınıflarına (metaclass'lar miras alınır), varsa sınıfın __metaclass__ özniteliğine (varsa) veya __metaclass__ global değişkenine bakarak 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 bir sınıfın türünü tanımlar, yalnızca bir fabrika değil, 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'ındaki bir yönteme örnektir. Sınıfın davranış şeklini uygulamak veya değiştirmek için __add__, __iter__ ve __getattr__ gibi normal 'büyü' yöntemlerini de tanımlayabilirsiniz.

İşte bit 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.__name__ + 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__
    
2512
2019-03-04 21: 34: 19Z
  1. class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
    2017-08-03 14: 34: 29Z
  2. ppperry, yazının kendisini bir metaclass olarak kullanmadan yazmayı yeniden oluşturamayacağınız anlamına geliyordu. Söyleyecek kadar adil olan.
    2018-09-18 23: 24: 10Z
  3. Örnek sınıf örneği tarafından unregister () çağrılmamalı mıydı?
    2018-11-29 00: 59: 15Z
  4. Python 3'te __metaclass__'un desteklenmediğini unutmayın. Python 3'te class MyObject(metaclass=MyType) kullanın, bkz. python.org/dev/peps/pep-3115 ve aşağıdaki yanıtla.
    2019-05-01 08: 36: 00Z

Nesne olarak sınıflar

Metasınıfları anlamadan önce, Python'da ders vermeniz gerekir. Python'un, Smalltalk dilinden ödünç alınan sınıfların ne olduğuna dair çok özel bir fikri var.

Ç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 fazladır. Sınıflar da nesnedir.

Evet, nesneler.

class anahtar sözcüğünü kullanır kullanmaz, Python çalıştırır ve oluşturur bir obje. Yönerge

 
>>> class ObjectCreator(object):
...       pass
...

bellekte "ObjectCreator" adında bir nesne oluşturur.

Bu nesnenin (sınıf) kendisi nesne oluşturma yeteneğine sahiptir (örnekler), ve bu yüzden bir sınıftır .

Ama yine de, bu bir nesne ve bu nedenle:

  • bir değişkene atayabilirsiniz
  • kopyalayabilirsiniz
  • buna özellikler ekleyebilirsiniz
  • bir işlev parametresi olarak iletebilirsiniz

örn.

 
>>> 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, bunları anında herhangi bir nesne gibi oluşturabilirsiniz.

Ö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 şeyle, el ile yapmanın bir yolunu gösterir.

type işlevini hatırlıyor musunuz? Ne olduğunu bilmenizi sağlayan eski fonksiyon bir nesneyi yazın:

 
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

Eh, type 'ın tamamen farklı bir yeteneği var. anında sınıfları oluşturun. type, bir sınıfın tanımını parametre olarak alabilir, ve bir sınıf döndürün.

(Biliyorum, aynı işlevin, kendinize ilettiğiniz parametrelere göre iki tamamen farklı kullanımı olması aptalca. Geriye dönük bir sorun var. Python'daki uyumluluğu)

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)

örn.

 
>>> class MyShinyClass(object):
...       pass

bu şekilde manuel olarak 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 adı olarak "MyShinyClass" kullandığımızı fark edeceksiniz ve sınıf referansını tutacak değişken olarak. Farklı olabilirler ancak işleri karmaşıklaştırmak için hiçbir neden yoktur.

type, sınıfın özniteliklerini tanımlamak için bir sözlük kabul eder. Yani:

 
>>> class Foo(object):
...       bar = True

Şuna çevrilebilir:

 
>>> Foo = type('Foo', (), {'bar':True})

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

şöyle olur:

 
>>> 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 bir fonksiyon tanımla Uygun imzayla, bir özellik 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 nesneler ve dinamik olarak dinamik bir sınıf oluşturabilirsiniz.

Bu, class anahtar sözcüğünü kullanırken Python'un yaptığı ve bir metaclass kullanarak yaptığı şeydir.

Metaclass nedir (nihayet)

Meta gözlükler, sınıfları oluşturan 'şeyler'dir.

Nesneleri oluşturmak için sınıfları tanımlarsınız, değil mi?

Ancak Python sınıflarının nesne olduğunu öğrendik.

Peki, metasınıflar bu nesneleri yaratan şeydir. Onlar sınıfların sınıfları. onları bu şekilde hayal edebilirsiniz:

 
MyClass = MetaClass()
my_object = MyClass()

type’un bunun gibi bir şey yapmanıza izin verdiğini gördünüz:

 
MyClass = type('MyClass', (), {})

Bunun nedeni type işlevi aslında bir metaclass olmasıdır. type metaclass Python, sahne arkasındaki tüm sınıfları oluşturmak için kullanır.

Şimdi neden haltın Type değil küçük harfle yazıldığını merak ediyor musunuz?

Sanırım, bunun yarattığı sınıf str ile tutarlılık sorunu. dizeleri nesneler ve tamsayılı nesneler yaratan sınıfı int. type yalnızca sınıf nesnelerini oluşturan sınıf.

Bunu __class__ özniteliğini kontrol ederek görebilirsiniz.

Her şey ve her şeyi kastediyorum, Python'da bir nesnedir. Bu, ints içerir dizeler, fonksiyonlar ve sınıflar. Hepsi nesnelerdir. Ve hepsinde var bir sınıftan oluşturuldu:

 
>>> 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, __class__’un __class__’u 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 Python'un kullandığı yerleşik metaclass sınıfıdır, ancak elbette kendi metaclass.

__metaclass__ niteliği

Python 2'de, bir sınıf yazarken __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, Foo sınıfını oluşturmak için metaclass'ı kullanır.

Dikkat et, çok zor.

Önce class Foo(object) yazarsınız, ancak Foo sınıf nesnesi oluşturulmaz henüz bellekte.

Python, sınıf tanımında __metaclass__'u arayacaktır. Eğer bulursa, Foo nesne sınıfını oluşturmak için kullanır. Olmazsa kullanacaktır  type, sınıfı oluşturmak için.

Birkaç tane okudefa.

Ne zaman:

 
class Foo(Bar):
    pass

Python aşağıdakileri yapar:

__metaclass__'da Foo özelliği var mı?

Evetse, bellekte bir sınıf nesnesi oluşturun (bir sınıf nesnesi dedim, burada benimle kal), Foo adını __metaclass__’da olanı kullanarak oluşturun.

Python __metaclass__'u bulamazsa, MODÜL düzeyinde __metaclass__'u arayacak ve aynı şeyi yapmaya çalışacaktır (ancak, yalnızca eski stil sınıflarında herhangi bir şey devralmayan sınıflar için).

Sonra __metaclass__'u bulamazsa, sınıf nesnesini oluşturmak için Bar'ların (ilk ebeveyn) kendi metaclass'ını (varsayılan type olabilir) kullanır.

__metaclass__ özelliğinin miras alınmayacağına dikkat edin, ebeveynin metaclass'ı (Bar.__class__) olacaktır. Bar, __metaclass__ ile Bar (type() değil) oluşturan type.__new__() niteliğini kullanırsa, alt sınıflar bu davranışı devralmaz.

Şimdi büyük soru, __metaclass__’a ne koyabilirsiniz?

Cevap: sınıf oluşturabilecek bir şey.

Ve bir sınıf ne yaratabilir? type veya alt sınıf veya kullanan herhangi bir şey.

Python 3'teki meta gözlükler

Meta camı ayarlama sözdizimi Python 3: 'te değiştirildi  

class Foo(object, metaclass=something):
    ...

yani. __metaclass__ özelliği artık, temel sınıflar listesindeki bir anahtar kelime argümanı lehine kullanılmamaktadır.

Meta gözlüklerin davranışı ancak büyük ölçüde aynıdı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ı sınıfı otomatik olarak değiştirmektir, oluşturulduğunda.

Bunu genellikle API'ler için yaparsınız; burada eşleşen sınıflar oluşturmak geçerli içerik.

Modülünüzdeki tüm sınıfların olduğuna karar verdiğiniz aptal bir örnek düşünün niteliklerini büyük harfle yazılmış olmalıdır. Birkaç yolu var bunu yapın, ancak bir yol __metaclass__'u modül düzeyinde ayarlamaktır.

Bu şekilde, bu modülün tüm sınıfları bu metaclass kullanılarak yaratılacak, ve metaclass'a tüm özellikleri büyük harfe dönüştürmesini söylemeliyiz.

Neyse ki, __metaclass__ gerçekte herhangi bir çağrılabilir olabilir, resmi sınıf (Biliyorum, adında 'sınıf' olan bir şey olması gerekmez. bir sınıf, git figürü ... ama yardımcı olur).

Böylece bir işlev 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, ancak 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)

Ama bu gerçekten OOP değil. type'u doğrudan ararız ve geçersiz kılmazız veya ebeveyni __new__ numaralı telefonu arayın. Hadi yapalım:

 
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)

Ekstra argüman upperattr_metaclass'u fark etmiş olabilirsiniz. Var özel bir şey yok: __new__ her zaman ilk parametre olarak tanımladığı sınıfı alır. Tıpkı, örneği ilk parametre olarak alan sıradan yöntemler için self'unuz veya sınıf yöntemleri için tanımlayıcı sınıfınız gibi.

Elbette, burada kullandığım isimler açıklık uğruna uzun, ama benzeri self için, tüm argümanların geleneksel isimleri vardır. Yani 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)

Kalıtımsallığı kolaylaştıracak super kullanarak daha temiz hale getirebiliriz (çünkü evet, metasınıflardan miras alan, türden miras alan metacamlara 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 python 3'te bu çağrıyı şu şekilde anahtar kelime argümanlarıyla yaparsanız, şöyle:

 
class Foo(object, metaclass=Thing, kwarg1=value1):
    ...

Bunu kullanmak için metaclass içindeki bu çevirir:

 
class Thing(type):
    def __new__(cls, clsname, bases, dct, kwargs1=default):
        ...

İşte bu. Metaclass hakkında daha fazla bir şey yok.

Metasınıfları kullanan kodun karmaşıklığının arkasındaki sebep, Metaclasses, çünkü genellikle bükülmüş işleri yapmak için metaclasses kullanıyorsunuz. introspeksiyona dayanarak, kalıtımın manipüle edilmesi, __dict__ gibi varyasyonlar vb.

Gerçekten, metasınıflar kara büyü yapmak için özellikle kullanışlıdır ve bu nedenle karmaşık şeyler. Ancak kendi başlarına, basittirler:

  • bir sınıf oluşturma işlemini engelleyin
  • sınıfı değiştirin
  • değiştirilen sınıfı döndürün

Neden işlevler yerine metasınıf sınıflarını kullandınız?

__metaclass__ herhangi bir numarayı kabul edebileceğinden, neden bir sınıf kullandınız? Açıkçası daha karmaşık olduğu için?

Bunu yapmak için birkaç neden var:

  • Amaç açık. UpperAttrMetaclass(type)'u okuduğunda, biliyorsun ne takip edecek
  • 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ı belirlediyseniz, ancak bir metaclass işleviyle belirtilmemişse, meta sınıfının örnekleri olur.
  • Kodunuzu daha iyi yapılandırabilirsiniz. Asla metaclass kullanmıyorsungibi bir şey için es Yukarıdaki örnek gibi önemsiz. Genellikle karmaşık bir şey için. Sahip olmak Birkaç yöntem yapma ve bunları bir sınıfta gruplama yeteneği çok faydalıdır kodu okumayı kolaylaştırmak için.
  • __new__, __init__ ve __call__'a bağlanabilirsiniz. Hangi izin verecek farklı şeyler yapman için. Genellikle hepsini __new__'da yapabilirseniz bile, bazı insanlar __init__'u kullanmaktan daha rahat.
  • Bunlara metasınıf 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 genelde yapmazsınız:

  

Metaclasses daha derin sihir   Kullanıcıların% 99'u asla endişelenmemelidir.   Onlara ihtiyacınız olup olmadığını merak ediyorsanız,   yapmazsın (aslında insanlar   kesin olarak bilmelerini isterim ki   Onlara ihtiyaçları var ve   neden hakkında açıklama).

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ız:

 
guy = Person(name='bob', age='35')
print(guy.age)

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__’u tanımlar ve Sadece basit ifadelerle tanımladığınız Person'u döndürecek bir sihir kullanır Bir veritabanı alanına karmaşık bir kancaya.

Django, basit bir API'yi açığa çıkararak karmaşık bir şeyin basit görünmesini sağlar ve metaclasses kullanarak, gerçek işi yapmak için bu API kodunu yeniden oluşturun perde arkasında.

Son kelime

Öncelikle, sınıfların örnekler oluşturabilecek nesneler olduğunu biliyorsunuz.

Aslında, sınıflar kendileridir. Metaclasses.

 
>>> class Foo(object): pass
>>> id(Foo)
142630324

Her şey Python'da bir nesnedir ve hepsi sınıfın örnekleridir veya metasınıf örnekleri.

type hariç.

type aslında kendi metaclass'tır. Bu yapabileceğin bir şey değil saf Python ile çoğaltmak ve uygulamada biraz hile yaparak yapılır düzeyi.

İkincisi, metasınıflar karmaşıktır. Onları kullanmak istemeyebilirsiniz çok basit sınıf değişiklikleri. İki farklı teknik kullanarak sınıfları değiştirebilirsiniz:

Sınıf değişikliği gerektiğinde% 99’u, bunları kullanmanız daha iyi olur.

Ancak, zamanın% 98’inde hiçbir şekilde sınıf değişikliği yapmanız gerekmez.

    
6304
2019-04-16 17: 55: 24Z
  1. Görünüşe göre Django models.Model'da __metaclass__'u değil, class Model(metaclass=ModelBase):'u, daha sonra yukarıda belirtilen metaclass sihrini uygulayan ModelBase sınıfını referans almak için kullandığı görülüyor. Harika yazı! İşte Django kaynağı: github.com/django /django /damla /ana /django /db /modeller /...
    2017-04-12 13: 18: 59Z
  2. hem taban hem de türetilmiş sınıfta metaclass farklıysa ne olur?
    2017-04-20 03: 24: 08Z
  3. &Burada; __metaclass__ özniteliğinin miras alınmayacağına dikkat edin, ana öğenin metaclass'ı (Bar.__class__) olacaktır. Bar, __metaclass__ ile Bar (type() değil) oluşturan type.__new__() niteliğini kullandıysa, alt sınıflar bu davranışı devralmaz. - Siz /birileri bu bölümü biraz daha açıklayabilir misiniz?
    2017-04-25 21: 32: 44Z
  4. @ MaxGoodridge Metaclasses için Python 3 sözdizimi. Bkz. Python 3.6 Veri modeli VS Python 2.7 Veri modeli
    2017-06-13 13: 22: 27Z
  5. Bu bir topluluk wiki cevabıdır (bu nedenle, düzeltmelere /geliştirmelere yorum yapanlar, yorumların doğru olup olmadıklarına göre cevabın içinde düzenlemeyi düşünebilir).
    2017-11-08 08: 59: 31Z

    Not, bu cevap Python 2.x için, 2008'de yazıldığı gibi, metasınıflar 3.x'ta biraz farklıdır.

    Metasınıflar 'sınıf' çalışmasını sağlayan gizli sosdur. Yeni bir stil nesnesinin varsayılan metaclass'ı 'type' olarak adlandırılır.

     
    class type(object)
      |  type(object) -> the object's type
      |  type(name, bases, dict) -> a new type
    

    Metasınıflar 3 aralık alır. ' ad ', ' bazlar ' ve ' dikte '

    İş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' işleminin 'metaclass init_attributes' a sahip olarak kazandığı sihir davranışının Initalised alt sınıfına geçmediğine dikkat edin.

    İşte sınıf oluşturulduğunda bir eylem gerçekleştiren bir metaclass yapmak için 'type' alt sınıfını nasıl sınıflandırabileceğ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
    
        
    359
    2019-05-13 07: 55: 44Z

    Meta gözlükler için bir kullanım, bir örneğe otomatik olarak bir örneğe yeni özellikler ve yöntemler eklemektir.

    Örneğin, Django modelleri 'ne bakarsanız, tanımı biraz 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)
    

    Ancak, çalışma zamanında Person nesneleri her türlü kullanışlı yöntemle doldurulur. Şaşırtıcı bir metaclassery için kaynağa bakın.     

    146
    2008-09-19 06: 45: 40Z
    1. Bir örnek değil, sınıfa yeni özellikler ve yöntemler ekleyen meta sınıfların kullanımı değil mi? Anladığım kadarıyla meta sınıfı sınıfın kendisini değiştirir ve sonuç olarak örnekler değiştirilmiş sınıf tarafından farklı şekillerde oluşturulabilir. Meta sınıfın doğasını almaya çalışan insanları biraz yanıltıcı olabilir. Örnekler üzerinde faydalı yöntemlere sahip olmak normal kalıtımla elde edilebilir. Örnek olarak Django koduna yapılan atıflar yine de iyidir.
      2017-01-27 23: 24: 42Z

    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. Bunu bir metaclass kullanarak yapmanın en kolay olduğunu gördüm.

     
    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’un 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.

        
    146
    2016-11-28 18: 04: 41Z
    1. Örnek için teşekkürler. Bunu neden __init__(self) type(self)._order = MyBase.counter; MyBase.counter += 1 diyor MyBase'den devralmaktan daha kolay buldunuz?
      2019-04-16 17: 59: 34Z
    2. Sınıfların kendilerinin değil, sınıflarının numaralandırılmasını istedim.
      2019-04-16 18: 09: 39Z
    3. Doğru, ah. Teşekkürler. Kodum, her başlatılışta MyType'ın özniteliğini sıfırlar ve bir MyType örneği asla oluşturulmazsa, özniteliği hiçbir zaman ayarlamaz. Hata. (Ve bir sınıf mülk de işe yarayabilirdi, but Metaclass'tan farklı olarak, sayacı saklamak için açık bir yer sunmaz.)
      2019-04-17 21: 58: 00Z

    ONLamp'ın metaclass programlamaya girişinin iyi yazılmış olduğunu düşünüyorum ve zaten birkaç yaşında olmasına rağmen konuyu gerçekten iyi bir şekilde tanıtıyor.

    http://www.onlamp.com/pub /a /python /2003/04/17 /metaclasses.html ( https://web.archive.org/web/20080206005253/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. Bu davranışı etkinleştirmek için Python sınıflarında birinci sınıf nesneler olması gerektiği de kolayca görülebilir.

    Hiç bir zaman kendim yazmadım, ancak metasınıfların en güzel kullanımlarından birinin Django çerçevesi içinde görülebileceğini düşünüyorum. . 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, onlara gereksinim duymama ihtimaliniz% 99'dur.

        
    106
    2018-08-13 04: 53: 15Z
      

    Metasınıf nedir? Onları ne için kullanıyorsunuz?

    TLDR: Bir metaclass, bir sınıftaki gibi bir sınıfın davranışını başlatır ve tanımlar; bir örnek için davranışı tanımlar ve tanımlar.

    Sözdekod:

     
    >>> Class(...)
    instance
    

    Yukarıdakiler tanıdık gelmelidir. Peki, Class nereden geliyor? Bu bir metaclass örneğidir (ayrıca sözde kod):

     
    >>> Metaclass(...)
    Class
    

    Gerçek kodda, type varsayılan metaclass'ı, bir sınıfı başlatmak için gereken her şeyi geçebiliriz ve bir sınıf alırız:

     
    >>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
    <class '__main__.Foo'>
    

    Farklı bir şekilde koymak

    • Bir sınıf, bir metaclass'ın bir sınıfa olduğu bir örnektir.

      Bir nesneyi başlattığımızda, bir örnek alırız:

       
      >>> object()                          # instantiation of class
      <object object at 0x7f9069b4e0b0>     # instance
      

      Aynı şekilde, varsayılan metaclass type ile açıkça bir sınıf tanımladığımızda, onu başlatırız:

       
      >>> type('Object', (object,), {})     # instantiation of metaclass
      <class '__main__.Object'>             # instance
      
    • Başka bir deyişle, sınıf bir metaclass örneğidir:

       
      >>> isinstance(object, type)
      True
      
    • Üçüncü bir yol koyun, bir sınıf bir sınıfın sınıfıdır.

       
      >>> type(object) == type
      True
      >>> object.__class__
      <class 'type'>
      

    Bir sınıf tanımı yazdığınızda ve Python bunu çalıştırdığında, sınıf nesnesini somutlaştırmak için bir metaclass kullanır (sırayla o sınıfın örneklerini somutlaştırmak için kullanılır).

    Özel nesne örneklerinin davranış şeklini değiştirmek için sınıf tanımlarını kullanabildiğimiz gibi, bir sınıf nesnesinin davranış şeklini değiştirmek için bir üst sınıf sınıfı tanımını kullanabiliriz.

    Ne için kullanılabilirler? dokümanlardan :

      

    Metasınıfların potansiyel kullanımları sınırsızdır. Araştırılan bazı fikirler arasında günlük kaydı, arabirim denetimi, otomatik yetkilendirme, otomatik özellik oluşturma, proxy'ler, çerçeveler ve otomatik kaynak kilitleme /senkronizasyon bulunur.

    Bununla birlikte, kullanıcıların kesinlikle gerekli olmadıkça metasınıfları kullanmaktan kaçınmaları önerilir.

    Her sınıf oluşturduğunuzda bir metaclass kullanırsınız:

    Bir sınıf tanımı yazdığınızda, örneğin, bunun gibi,

     
    class Foo(object): 
        'demo'
    

    Bir sınıf nesnesini başlatıyorsunuz.

     
    >>> Foo
    <class '__main__.Foo'>
    >>> isinstance(Foo, type), isinstance(Foo, object)
    (True, True)
    

    İşlevsel olarak type'u uygun argümanlarla çağırmak ve sonucu bu adın değişkenine atamakla aynıdır:

     
    name = 'Foo'
    bases = (object,)
    namespace = {'__doc__': 'demo'}
    Foo = type(name, bases, namespace)
    

    Not: __dict__’a, yani ad alanına otomatik olarak bazı şeyler eklenir:

     
    >>> Foo.__dict__
    dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
    '__module__': '__main__', '__weakref__': <attribute '__weakref__' 
    of 'Foo' objects>, '__doc__': 'demo'})
    
    Her iki durumda da oluşturduğumuz nesnenin

    metaclass değeri type'dur.

    (__dict__ sınıfının içeriği hakkında bir not: __module__ orada çünkü sınıflar nerede tanımlandıklarını bilmeli ve __dict__ ve __weakref__ da orada çünkü __slots__'u tanımlamıyoruz - eğer __slots__ , örneklerde biraz yer kazanacağız, çünkü __dict__ ve __weakref__'u hariç tutabiliriz. Örneğin:

     
    >>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
    >>> Baz.__dict__
    mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})
    

    ... ama ben dalıyorum.)

    type'u diğer sınıf tanımları gibi uzatabiliriz:

    İşte varsayılan __repr__ sınıfı:

     
    >>> Foo
    <class '__main__.Foo'>
    

    Bir Python nesnesi yazarken varsayılan olarak yapabileceğimiz en değerli şeylerden biri, __repr__ ürününü iyi sağlamaktır. help(repr)'u aradığımızda, __repr__ için eşitlik testi de gerektiren obj == eval(repr(obj)) için iyi bir test olduğunu öğreniyoruz - __repr__. Tip sınıfımızın sınıf örnekleri için __eq__ ve __repr__'un aşağıdaki basit uygulaması, varsayılan

    class Type(type):
        def __repr__(cls):
            """
            >>> Baz
            Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
            >>> eval(repr(Baz))
            Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
            """
            metaname = type(cls).__name__
            name = cls.__name__
            parents = ', '.join(b.__name__ for b in cls.__bases__)
            if parents:
                parents += ','
            namespace = ', '.join(': '.join(
              (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
                   for k, v in cls.__dict__.items())
            return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
        def __eq__(cls, other):
            """
            >>> Baz == eval(repr(Baz))
            True            
            """
            return (cls.__name__, cls.__bases__, cls.__dict__) == (
                    other.__name__, other.__bases__, other.__dict__)
    
    sınıfında gelişebilecek bir gösteri sağlar:  __repr__

    Şimdi bu metaclass ile bir nesne yarattığımızda, komut satırında yankılanan

    >>> class Bar(object): pass
    >>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
    >>> Baz
    Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
    
    , varsayılandan çok daha az çirkin bir görüş sağlıyor:  __repr__

    Sınıf örneği için tanımlanmış hoş bir eval(repr(Class)) ile, kodumuzu hata ayıklama konusunda daha güçlü bir yeteneğimiz var. Ancak, __repr__ ile daha fazla kontrol yapılması pek mümkün değildir (işlevler varsayılan __prepare__'lardan değerlendirmek için oldukça imkansız olacağından).

    Beklenen bir kullanım: __prepare__ bir ad alanı

    Örneğin, bir sınıfın yöntemlerinin hangi sırayla oluşturulduğunu bilmek istiyorsak, sınıfın ad alanı olarak sıralı bir dit sağlayabiliriz. Bunu, ad alanı diktesini döndüren

    from collections import OrderedDict
    
    class OrderedType(Type):
        @classmethod
        def __prepare__(metacls, name, bases, **kwargs):
            return OrderedDict()
        def __new__(cls, name, bases, namespace, **kwargs):
            result = Type.__new__(cls, name, bases, dict(namespace))
            result.members = tuple(namespace)
            return result
    
    ile yapardık Python 3'te uygulanmışsa, sınıf :

     
    class OrderedMethodsObject(object, metaclass=OrderedType):
        def method1(self): pass
        def method2(self): pass
        def method3(self): pass
        def method4(self): pass
    

    Ve kullanım:

     
    >>> OrderedMethodsObject.members
    ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')
    

    Ve şimdi, bu yöntemlerin (ve diğer sınıf niteliklerinin) yaratıldığı sıraya dair bir kayda sahibiz:

     
    >>> inspect.getmro(OrderedType)
    (<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)
    

    Not, bu örnek belgelerinden - uyarlanmıştır yeni standart kütüphanede enum yapar.

    Yani yaptığımız şey, bir sınıf oluşturarak bir metaclass örneğini başlatmaktı. Ayrıca metaclass'ı diğer sınıflardaki gibi ele alabiliriz. Bir yöntem çözme siparişi var:

     repr

    Ve yaklaşık olarak

    >>> OrderedMethodsObject
    OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
    hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
    ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
    
    değerine sahiptir (işlevlerimizi temsil etmenin bir yolunu bulamadıkça artık değerlendiremeyiz.):  __prepare__     
    93
    2017-08-30 03: 19: 14Z

    Python 3 güncellemesi

    Bir metaclass'ta (bu noktada) iki anahtar yöntem vardır:

    •  __new__ ve
    • __prepare__

    OrderedDict, sınıf oluşturulurken ad alanı olarak kullanılacak özel bir eşleme (__prepare__ gibi) sağlamanıza olanak tanır. Seçtiğiniz herhangi bir ad alanının bir örneğini döndürmelisiniz. dict'u uygulamazsanız normal bir __new__ kullanılır.

    class Meta(type):
    
        def __prepare__(metaclass, cls, bases):
            return dict()
    
        def __new__(metacls, cls, bases, clsdict):
            return super().__new__(metacls, cls, bases, clsdict)
    
    , final sınıfının fiili oluşturulmasından /değiştirilmesinden sorumludur.

    Çıplak kemikler ve hiçbir şey yapmadan ekstra bir metaclass istiyorum:

     int

    Basit bir örnek:

    Bazı basit doğrulama kodunun niteliklerinizde çalışmasını istediğinizi söyleyin - her zaman str veya

    class Person:
        weight = ValidateType('weight', int)
        age = ValidateType('age', int)
        name = ValidateType('name', str)
    
    olması gerektiği gibi. Bir metaclass olmadan, sınıfınız şunun gibi görünür:  
    class Person(metaclass=Validator):
        weight = ValidateType(int)
        age = ValidateType(int)
        name = ValidateType(str)
    

    Görebileceğiniz gibi, özniteliğin adını iki kez tekrarlamanız gerekir. Bu, tahriş edici hataları ile birlikte yazım hataları mümkün kılar.

    Basit bir metaclass bu sorunu çözebilir:

     __prepare__

    Metaclass'ın neye benzeyeceği budur (gerekmediğinden

    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)
    
    kullanmıyorsunuz):  
    p = Person()
    p.weight = 9
    print(p.weight)
    p.weight = '9'
    

    Örnek bir çalışma:

     
    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')
    

    üretir:

     
    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
    

    Not : Bu örnek bir sınıf dekoratörüyle de yapılabilecek kadar basittir, ancak muhtemelen gerçek bir metaclass daha fazlasını yapıyor olabilir.

    Başvuru için 'ValidateType' sınıfı:

     __call__()     
    67
    2016-03-01 19: 48: 34Z

    Sınıf örneği oluştururken 'metaclass'ın rolü'
    # 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')
    
    yöntemi

    Python programlamayı birkaç aydan daha uzun bir süre boyunca yaptıysanız, sonunda şuna benzer bir kod yazmanız gerekir:

     __call__()

    İkincisi p

    class SomeClass(object):
        # ...
        # some definition here ...
        # ...
    
        def __call__(self, foo, bar):
            return bar + foo
    
    sihirli yöntemini sınıfta uyguladığınızda olasıdır.  __call__()

    __call__() yöntemi, bir sınıfın bir örneği çağrılabilir olarak kullanıldığında ç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ştururken), aslında metaclass'ın instance = SomeClass() yöntemini çağırıyoruz. Bu noktada çoğu Python programcısının kafası biraz karışık çünkü __init__() gibi bir örnek oluştururken __init__() yöntemini çağırdığınızı söylemişlerdi. Biraz daha derine inen, __new__()'dan önce __new__() olduğunu biliyor. Eh, bugün başka bir doğruluk katmanı ortaya çıkıyor, __call__()'dan önce '

    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
    
    metaclass'ı var.

    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 vermek üzere olduğu anı kaydeden bir metaclass.

     
    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__()."
    

    Bu, bu metaclass'ı kullanan bir sınıftır

     Class_1

    Şimdi bir

    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.
    
    örneği oluşturalım  type

    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. Meta_1, type'un ana sınıfı (type.__call__(), varsayılan ana metaclass) olduğundan ve yukarıdaki çıktının sipariş sırasını göz önünde bulundurarak, şimdi

    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
    
    'un sahte uygulaması ne olacağına dair bir ipucumuz var:  __call__()

    Metaclass '__new__() yönteminin ilk olarak adlandırılan yöntem olduğunu görebiliriz. Daha sonra, örneğin oluşturulmasını sınıfın __init__() yöntemine ve örneklemin __call__() una başlatılmasını sağlar. Aynı zamanda örneği döndüren oydu.

    Yukarıdan itibaren, Class_1.__new__() metaclass'ına ayrıca Class_1.__init__() veya

    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__()."
    
    'a bir çağrı yapılıp yapılmayacağına karar verme fırsatı verilir. Uygulaması boyunca bu yöntemlerden herhangi biriyle dokunulmamış bir nesneyi geri getirebilirdi. Örneğin bu yaklaşımı singleton modeline uygulayın:  Class_2

    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
    
    türünde bir nesne oluşturmaya çalışırken art arda ne olduğunu gözlemleyelim  
    #!/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()
    
        
    58
    2018-08-27 17: 21: 01Z

    Metaclass, başka bir sınıfın nasıl (bazı) oluşturulması gerektiğini söyleyen bir sınıftır.

    Bu, metaclass'ı sorunuma bir çözüm olarak gördüğümde: Gerçekten karmaşık bir problemim vardı, muhtemelen farklı şekilde çözülebilirdi, fakat bunu 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 ...

     type     
    50
    2016-01-25 20: 08: 37Z

    metaclass aslında metaclass - başka bir sınıf oluşturan bir sınıftır. Çoğu type, metaclass'un alt sınıflarıdır. new,

    >>> 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'
    __qualname__ :'NewClass'
    
    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:  Note:

    metaclass

    Sınıfın herhangi bir zamanda somutlaştırılmadığına dikkat edin; sınıfı oluşturma işlemi, type(obj)'un yürütülmesini tetikledi.

        
    36
    2017-08-29 05: 23: 15Z

    tl; dr sürümü

    type() işlevi size bir nesnenin türünü verir.

    Bir sınıfın

    class Foo(object):
        __metaclass__ = MyMetaClass
    
    ’u, metaclass.’de

    'dir.

    Bir metaclass kullanmak için:

     
    class foo:
        ...
    
        
    32
    2016-12-30 17: 28: 40Z

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

    meta class, 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 eşlenmiş bir sınıfta olmasını istediğiniz (alanlar, iş kuralları, vb. ö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ı yazın ve mantığınızı eklemek için aşağıdaki sihirli yöntemleri geçersiz kılabilirsiniz.

     
    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
    

    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.

        
    22
    2017-07-13 08: 18: 22Z

    type () işlevi bir nesnenin türünü döndürebilir veya yeni bir tür oluşturabilir,

    örneğin, type () işleviyle bir Hi sınıfı oluşturabiliriz ve bu şekilde Hi (object) sınıfıyla kullanmaya gerek yoktur:

     
    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']
    

    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. Bu, türün, yerleşik sınıfların çoğunun metaclass'ı ve kullanıcı tanımlı sınıfların metaclass'ıdır.

     metaclass

    Magic, metaclass'taki anahtar kelime argümanlarını ilettiğimizde yürürlüğe girecek, Python yorumlayıcısını ListMetaclass aracılığıyla CustomList oluşturmak için gösterir. yeni (), 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
    2018-01-12 09: 30: 20Z

    Yayınlanan cevaplara ek olarak class'un bir sınıfın davranışını tanımladığını söyleyebilirim. Böylece, açıkça metaclass'ınızı ayarlayabilirsiniz. Ne zaman Python metaclass anahtar kelimesi alırsa, __metaclass__ kelimesini aramaya başlar. Bulunmazsa - varsayılan metaclass tipi sınıfın nesnesini oluşturmak için kullanılır. metaclass özelliğini kullanarak, sınıfınızın

    class MyClass:
       __metaclass__ = type
       # write here other method
       # write here one more method
    
    print(MyClass.__metaclass__)
    
    'unu ayarlayabilirsiniz:  
    class 'type'
    

    Bu şekilde çıktı üretecek:

     metaclass

    Ve elbette, sınıfınızı kullanarak oluşturulan herhangi bir sınıfın davranışını tanımlamak için kendi metaclass'unuzu da oluşturabilirsiniz.

    Bunu yapmak için, varsayılan metaclass tür sınıfınızın ana

    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__)
    
    olduğu için miras alınması gerekir:  
    class '__main__.MyMetaClass'
    class 'type'
    

    Çıktı şöyle olacak:

          
    7
    2018-09-15 13: 17: 29Z
kaynak yerleştirildi İşte