python - पायथन में मेटाक्लास क्या हैं?





oop metaclass python-datamodel (13)


एक मेटाक्लास एक वर्ग है जो बताता है कि कैसे (कुछ) अन्य वर्ग बनाया जाना चाहिए।

यह एक ऐसा मामला है जहां मैंने मेटाक्लास को मेरी समस्या के समाधान के रूप में देखा: मुझे वास्तव में जटिल समस्या थी, शायद इसे अलग-अलग हल किया जा सकता था, लेकिन मैंने मेटाक्लास का उपयोग करके इसे हल करना चुना। जटिलता के कारण, यह मेरे द्वारा लिखे गए कुछ मॉड्यूल में से एक है जहां मॉड्यूल में टिप्पणियां लिखी गई कोड की मात्रा को पार करती हैं। यह रहा...

#!/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()

मेटाक्लास क्या हैं और हम उनके लिए क्या उपयोग करते हैं?




__call__()कक्षा उदाहरण बनाते समय मेटाक्लास ' विधि की भूमिका

यदि आपने कुछ महीनों से अधिक समय तक पाइथन प्रोग्रामिंग किया है तो आप अंततः कोड पर ठोकर खाएंगे जो इस तरह दिखता है:

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

उत्तरार्द्ध संभव है जब आप __call__()कक्षा पर जादू विधि लागू करते हैं ।

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

__call__()विधि जब एक वर्ग का एक उदाहरण एक प्रतिदेय के रूप में प्रयोग किया जाता है शुरू हो जाती है। लेकिन जैसा कि हमने पिछले उत्तरों से देखा है, एक वर्ग स्वयं मेटाक्लास का एक उदाहरण है, इसलिए जब हम कक्षा को कॉल करने योग्य के रूप में उपयोग करते हैं (यानी जब हम इसका उदाहरण बनाते हैं) हम वास्तव में इसकी मेटाक्लास __call__()विधि को बुला रहे हैं । इस बिंदु पर अधिकांश पायथन प्रोग्रामर थोड़ा उलझन में हैं क्योंकि उन्हें बताया गया है कि इस तरह एक उदाहरण बनाते समय instance = SomeClass()आप इसकी __init__()विधि बुला रहे हैं । कुछ है जो एक सा गहरा खोदा गया है पता है कि इससे पहले कि __init__()वहाँ __new__()। खैर, आज __new__()मेटाक्लास के पहले सत्य की एक और परत प्रकट हो रही है __call__()

चलिए एक वर्ग के उदाहरण बनाने के परिप्रेक्ष्य से विधि कॉल श्रृंखला का अध्ययन करते हैं।

यह एक मेटाक्लास है जो एक उदाहरण बनने से पहले ठीक उसी क्षण लॉग करता है और जिस क्षण यह इसे वापस करने वाला है।

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

यह एक वर्ग है जो उस मेटाक्लास का उपयोग करता है

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

और अब चलिए एक उदाहरण बनाते हैं Class_1

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.

निरीक्षण करें कि ऊपर दिया गया कोड वास्तव में कार्यों को लॉग इन करने से अधिक कुछ नहीं करता है। प्रत्येक विधि वास्तविक कार्य को अपने माता-पिता के कार्यान्वयन में प्रस्तुत करती है, इस प्रकार डिफ़ॉल्ट व्यवहार को ध्यान में रखती है। चूंकि typeयह है Meta_1की माता पिता वर्ग ( typeडिफ़ॉल्ट माता पिता metaclass जा रहा है) और इसके बाद के संस्करण उत्पादन के आदेश अनुक्रम पर विचार, अब हम क्या की छद्म कार्यान्वयन किया जाएगा के रूप में एक सुराग है type.__call__():

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

हम देख सकते हैं कि मेटाक्लास ' __call__()विधि वह है जिसे पहले कहा जाता है। इसके बाद यह उदाहरण के निर्माण के लिए कक्षा की __new__()विधि और प्रारंभिकरण के उदाहरण का निर्माण करता है __init__()। यह वह भी है जो आखिरकार उदाहरण देता है।

उपर्युक्त से यह उपजी है कि मेटाक्लास __call__()को यह तय करने का मौका भी दिया जाता है कि कॉल करने Class_1.__new__()या Class_1.__init__()अंत में किया जाएगा या नहीं । इसके निष्पादन के दौरान यह वास्तव में एक ऐसी वस्तु को वापस कर सकता है जिसे इन तरीकों में से किसी एक द्वारा छुआ नहीं गया है। उदाहरण के लिए सिंगलटन पैटर्न के लिए यह दृष्टिकोण लें:

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

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



प्रकार () फ़ंक्शन किसी ऑब्जेक्ट के प्रकार को वापस कर सकता है या एक नया प्रकार बना सकता है,

उदाहरण के लिए, हम प्रकार () फ़ंक्शन के साथ एक हाय क्लास बना सकते हैं और कक्षा हाय (ऑब्जेक्ट) के साथ इस तरह उपयोग करने की आवश्यकता नहीं है:

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

कक्षाओं को गतिशील रूप से बनाने के लिए प्रकार () का उपयोग करने के अलावा, आप वर्ग के निर्माण व्यवहार को नियंत्रित कर सकते हैं और मेटाक्लास का उपयोग कर सकते हैं।

पायथन ऑब्जेक्ट मॉडल के अनुसार, कक्षा वस्तु है, इसलिए कक्षा एक और निश्चित वर्ग का एक उदाहरण होना चाहिए। डिफ़ॉल्ट रूप से, एक पायथन कक्षा प्रकार वर्ग का उदाहरण है। यही है, टाइप किए गए अधिकांश वर्गों और उपयोगकर्ता परिभाषित वर्गों के मेटाक्लास का मेटाक्लास है।

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

जब हम मेटाक्लास में कीवर्ड तर्क पारित करते हैं तो मैजिक प्रभावी होगा, यह लिस्टोन दुभाषिया को ListMetaclass के माध्यम से कस्टमलिस्ट बनाने के लिए इंगित करता है। नया (), इस बिंदु पर, हम वर्ग परिभाषा को संशोधित कर सकते हैं, उदाहरण के लिए, और एक नई विधि जोड़ें और फिर संशोधित परिभाषा को वापस कर दें।




typeवास्तव में एक है metaclass- एक वर्ग जो एक और वर्ग बनाता है। अधिकांश metaclassउप-वर्ग हैं typemetaclassप्राप्त करता है 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'

Note:

ध्यान दें कि कक्षा किसी भी समय तत्काल नहीं थी; वर्ग बनाने के सरल कार्य के निष्पादन को ट्रिगर किया गया metaclass




पायथन 3 अपडेट

मेटाक्लास में दो मुख्य विधियां हैं (इस बिंदु पर):

  • __prepare__ , तथा
  • __new__

__prepare__आपको OrderedDictकक्षा के निर्माण के दौरान नामस्थान के रूप में उपयोग करने के लिए कस्टम मैपिंग (जैसे ए) की आपूर्ति करने देता है । आपको जो भी नामस्थान चुनते हैं उसका एक उदाहरण वापस करना होगा। यदि आप __prepare__सामान्य लागू नहीं करते हैं तो dictइसका उपयोग किया जाता है।

__new__ अंतिम श्रेणी के वास्तविक निर्माण / संशोधन के लिए जिम्मेदार है।

एक नंगे हड्डियों, कुछ भी नहीं-अतिरिक्त मेटाक्लास पसंद करेंगे:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

एक साधारण उदाहरण:

मान लें कि आप अपने विशेषताओं पर चलाने के लिए कुछ सरल सत्यापन कोड चाहते हैं - जैसे कि यह हमेशा एक intया एक होना चाहिए str। मेटाक्लास के बिना, आपकी कक्षा कुछ इस तरह दिखेगी:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

जैसा कि आप देख सकते हैं, आपको दो बार विशेषता का नाम दोहराना होगा। यह चिड़चिड़ाहट कीड़े के साथ टाइपो संभव बनाता है।

एक साधारण मेटाक्लास उस समस्या को संबोधित कर सकता है:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

मेटाक्लास इस तरह दिखेगा (इसका उपयोग नहीं कर रहा है __prepare__क्योंकि इसकी आवश्यकता नहीं है):

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)

का एक नमूना रन:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

पैदा करता है:

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

नोट : यह उदाहरण इतना आसान है कि इसे कक्षा सजावट के साथ भी पूरा किया जा सकता था, लेकिन संभवतः एक वास्तविक मेटाक्लास बहुत अधिक कर रहा होगा।

संदर्भ के लिए 'ValidateType' वर्ग:

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



मेटाक्लास के लिए एक उपयोग स्वचालित रूप से एक उदाहरण के लिए नई गुण और विधियों को जोड़ रहा है।

उदाहरण के लिए, यदि आप Django मॉडल देखते हैं , तो उनकी परिभाषा थोड़ा उलझन में दिखती है। ऐसा लगता है कि आप केवल वर्ग गुणों को परिभाषित कर रहे हैं:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

हालांकि, रनटाइम पर व्यक्ति वस्तुएं उपयोगी विधियों के सभी प्रकार से भरे हुए हैं। कुछ अद्भुत मेटाक्लासरी के लिए source देखें।




मुझे लगता है कि मेटाक्लास प्रोग्रामिंग के लिए ऑनलैम्प परिचय अच्छी तरह से लिखा गया है और कई साल पहले होने के बावजूद विषय के लिए वास्तव में एक अच्छा परिचय देता है।

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 पर संग्रहीत https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html )

संक्षेप में: एक वर्ग एक उदाहरण के निर्माण के लिए एक ब्लूप्रिंट है, एक वर्ग के निर्माण के लिए मेटाक्लास एक ब्लूप्रिंट है। यह आसानी से देखा जा सकता है कि पाइथन कक्षाओं में इस व्यवहार को सक्षम करने के लिए प्रथम श्रेणी की वस्तुओं की भी आवश्यकता होती है।

मैंने कभी खुद को नहीं लिखा है, लेकिन मुझे लगता है कि मेटाग्लास के सबसे अच्छे उपयोगों में से एक Django ढांचे में देखा जा सकता है। मॉडल क्लासेस नए मॉडल या फॉर्म क्लास लिखने की घोषणात्मक शैली को सक्षम करने के लिए मेटाक्लास दृष्टिकोण का उपयोग करते हैं। जबकि मेटाक्लास वर्ग बना रहा है, सभी सदस्यों को कक्षा को स्वयं अनुकूलित करने की संभावना मिलती है।

जो बात कहना बाकी है वह है: यदि आपको नहीं पता कि मेटाक्लास क्या हैं, तो संभावना है कि आपको उनकी आवश्यकता नहीं होगी 99%।




दूसरों ने समझाया है कि मेटाक्लास कैसे काम करते हैं और वे पाइथन प्रकार प्रणाली में कैसे फिट होते हैं। यहां एक उदाहरण दिया गया है जिसका उपयोग उनके लिए किया जा सकता है। मैंने लिखा एक परीक्षण ढांचे में, मैं उस क्रम का ट्रैक रखना चाहता था जिसमें कक्षाओं को परिभाषित किया गया था, ताकि मैं बाद में उन्हें इस क्रम में तुरंत चालू कर सकूं। मुझे मेटाक्लास का उपयोग करके ऐसा करना सबसे आसान लगता है।

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 उप-वर्ग है, उसके बाद कक्षा विशेषता _order जो उस क्रम को रिकॉर्ड करता है जिसमें कक्षाएं परिभाषित की गई थीं।




पाइथन कक्षाएं स्वयं वस्तुएं हैं - उदाहरण के तौर पर - उनके मेटा-क्लास के।

डिफ़ॉल्ट मेटाक्लास, जिसे तब लागू किया जाता है जब आप कक्षाओं को निर्धारित करते हैं:

class foo:
    ...

मेटा क्लास का उपयोग कक्षाओं के पूरे समूह में कुछ नियम लागू करने के लिए किया जाता है। उदाहरण के लिए, मान लीजिए कि आप किसी डेटाबेस तक पहुंचने के लिए ओआरएम बना रहे हैं, और आप प्रत्येक तालिका से रिकॉर्ड्स को उस तालिका में मैप किए गए वर्ग के रूप में चाहते हैं (फ़ील्ड्स, बिजनेस नियम, इत्यादि के आधार पर), मेटाक्लास के संभावित उपयोग उदाहरण के लिए, कनेक्शन पूल तर्क, जो सभी तालिकाओं से रिकॉर्ड के सभी वर्गों द्वारा साझा किया जाता है। एक और उपयोग विदेशी कुंजी का समर्थन करने के लिए तर्क है, जिसमें रिकॉर्ड के कई वर्ग शामिल हैं।

जब आप मेटाक्लास को परिभाषित करते हैं, तो आप उप-प्रकार टाइप करते हैं, और अपने तर्क को सम्मिलित करने के लिए निम्न जादू विधियों को ओवरराइड कर सकते हैं।

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

किसी भी तरह, वे दो सबसे अधिक इस्तेमाल किए जाने वाले हुक हैं। मेटाक्लासिंग शक्तिशाली है, और उपरोक्त कहीं भी मेटाक्लासिंग के लिए उपयोगों की विस्तृत और विस्तृत सूची नहीं है।




नोट, यह उत्तर Python 2.x के लिए है क्योंकि यह 2008 में लिखा गया था, मेटाक्लास 3.x में थोड़ा अलग हैं, टिप्पणियां देखें।

Metaclasses गुप्त सॉस हैं जो 'वर्ग' काम करते हैं। एक नई शैली वस्तु के लिए डिफ़ॉल्ट मेटाक्लास को 'प्रकार' कहा जाता है।

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

मेटाक्लास 3 तर्क लेते हैं। ' नाम ', ' आधार ' और ' dict '

यहां वह जगह है जहां रहस्य शुरू होता है। इस उदाहरण वर्ग परिभाषा में नाम, आधार और निर्देश कहां से आते हैं।

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

आइए एक मेटाक्लास को परिभाषित करें जो दिखाएगा कि ' वर्ग: ' इसे कैसे कॉल करता है।

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'

और अब, एक उदाहरण जिसका वास्तव में कुछ मतलब है, यह स्वचालित रूप से कक्षा में सेट "विशेषताएँ" सूची में चर बना देगा, और किसी भी पर सेट नहीं होगा।

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

ध्यान दें कि मेटाक्लास init_attributes होने से प्राप्त 'व्यवहार' लाभ का जादू व्यवहार इनटाइलाइज्ड के उप-वर्ग पर पारित नहीं होता है।

यहां एक और अधिक ठोस उदाहरण दिया गया है, यह दर्शाता है कि क्लास बनने पर कार्रवाई करने वाले मेटाक्लास को बनाने के लिए आप 'टाइप' को कैसे उपclass कर सकते हैं। यह काफी मुश्किल है:

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



प्रकाशित उत्तरों के अलावा मैं कह सकता हूं कि metaclassएक वर्ग के लिए व्यवहार को परिभाषित करता है। तो, आप स्पष्ट रूप से अपना मेटाक्लास सेट कर सकते हैं। जब भी पाइथन एक कीवर्ड प्राप्त करता है classतो यह खोजना शुरू कर देता है metaclass। यदि यह नहीं मिला है - डिफ़ॉल्ट मेटाक्लास प्रकार का उपयोग वर्ग की ऑब्जेक्ट बनाने के लिए किया जाता है। __metaclass__विशेषता का उपयोग करके , आप metaclassअपनी कक्षा का सेट कर सकते हैं :

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

यह इस तरह के आउटपुट का उत्पादन करेगा:

class 'type'

और, ज़ाहिर है, आप metaclassअपनी कक्षा का उपयोग कर बनाई गई किसी भी कक्षा के व्यवहार को परिभाषित करने के लिए अपना खुद का निर्माण कर सकते हैं ।

ऐसा करने के लिए, आपका डिफ़ॉल्ट metaclassप्रकार वर्ग विरासत में होना चाहिए क्योंकि यह मुख्य है metaclass:

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

आउटपुट होगा:

class '__main__.MyMetaClass'
class 'type'



मेटाक्लास क्या हैं? आप इन्हें किसके लिए इस्तेमाल करते हैं?

टीएलडीआर: एक मेटाक्लास एक कक्षा के लिए व्यवहार को तुरंत चालू करता है और परिभाषित करता है जैसे कि कक्षा तुरंत शुरू होती है और एक उदाहरण के लिए व्यवहार को परिभाषित करती है।

स्यूडोकोड:

>>> Class(...)
instance

उपरोक्त परिचित दिखना चाहिए। खैर, Class कहां से आती है? यह मेटाक्लास (एक छद्म कोड) का एक उदाहरण है:

>>> Metaclass(...)
Class

वास्तविक कोड में, हम डिफॉल्ट मेटाक्लास, type , सब कुछ जो हमें कक्षा को तुरंत चालू करने के लिए आवश्यक है और हमें कक्षा मिल सकती है:

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

इसे अलग रखो

  • एक वर्ग एक उदाहरण के रूप में एक वर्ग के लिए मेटाक्लास है।

    जब हम किसी ऑब्जेक्ट को तुरंत चालू करते हैं, तो हमें एक उदाहरण मिलता है:

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

    इसी प्रकार, जब हम डिफ़ॉल्ट मेटाक्लास के साथ स्पष्ट रूप से कक्षा को परिभाषित करते हैं, तो type , हम इसे तुरंत चालू करते हैं:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • एक और तरीका रखो, एक वर्ग मेटाक्लास का एक उदाहरण है:

    >>> isinstance(object, type)
    True
    
  • एक तीसरा रास्ता रखो, एक मेटाक्लास एक वर्ग की कक्षा है।

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

जब आप कक्षा परिभाषा लिखते हैं और पायथन इसे निष्पादित करता है, तो यह क्लास ऑब्जेक्ट को तुरंत चालू करने के लिए मेटाक्लास का उपयोग करता है (जो बदले में उस वर्ग के उदाहरणों को तुरंत चालू करने के लिए उपयोग किया जाएगा)।

जैसे ही हम क्लास परिभाषाओं का उपयोग कर सकते हैं कि कस्टम ऑब्जेक्ट इंस्टेंस कैसे व्यवहार करते हैं, हम क्लास ऑब्जेक्ट के तरीके को बदलने के लिए मेटाक्लास क्लास परिभाषा का उपयोग कर सकते हैं।

उनके लिए क्या उपयोग किया जा सकता है? docs :

मेटाक्लास के लिए संभावित उपयोग असीमित हैं। कुछ विचारों का पता लगाया गया है जिनमें लॉगिंग, इंटरफ़ेस जांच, स्वचालित प्रतिनिधिमंडल, स्वचालित संपत्ति निर्माण, प्रॉक्सी, ढांचे, और स्वचालित संसाधन लॉकिंग / सिंक्रनाइज़ेशन शामिल हैं।

फिर भी, उपयोगकर्ताओं को आमतौर पर मेटाक्लास का उपयोग करने से बचने के लिए प्रोत्साहित किया जाता है जब तक कि बिल्कुल जरूरी न हो।

हर बार जब आप कक्षा बनाते हैं तो आप मेटाक्लास का उपयोग करते हैं:

जब आप कक्षा परिभाषा लिखते हैं, उदाहरण के लिए, इस तरह,

class Foo(object): 
    'demo'

आप कक्षा वस्तु को तुरंत चालू करते हैं।

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

यह उचित तर्क के साथ कार्यात्मक रूप से कॉलिंग type जैसा ही है और परिणाम को उस नाम के चर के लिए असाइन करना है:

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

नोट, कुछ चीजें स्वचालित रूप से __dict__ , यानी नामस्थान में जोड़ दी __dict__ :

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

हमारे द्वारा बनाए गए ऑब्जेक्ट का मेटाक्लास , दोनों मामलों में, type

(कक्षा __dict__ की सामग्री पर एक साइड-नोट: __module__ वहां है क्योंकि कक्षाओं को पता होना चाहिए कि उन्हें कहां परिभाषित किया गया है, और __dict__ और __weakref__ वहां हैं क्योंकि हम __slots__ को परिभाषित नहीं करते हैं - अगर हम __slots__ को परिभाषित करते हैं तो हम थोड़ा सा बचाएंगे उदाहरणों में स्थान, क्योंकि हम उन्हें छोड़कर __dict__ और __weakref__ को अस्वीकार कर सकते हैं। उदाहरण के लिए:

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

... लेकिन मैं पीछे हटा।)

हम किसी भी अन्य वर्ग परिभाषा की तरह type विस्तार कर सकते हैं:

कक्षाओं का डिफ़ॉल्ट __repr__ यहां दिया गया है:

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

पाइथन ऑब्जेक्ट लिखने में डिफ़ॉल्ट रूप से हम सबसे मूल्यवान चीजों में से एक है इसे एक अच्छा __repr__ प्रदान करना है। जब हम help(repr) कहते help(repr) हम सीखते हैं कि __repr__ लिए एक अच्छा परीक्षण है जिसके लिए समानता के लिए परीक्षण की आवश्यकता होती है - obj == eval(repr(obj)) ।के निम्नलिखित सरल कार्यान्वयन __repr__और __eq__हमारे प्रकार वर्ग के वर्ग उदाहरण के लिए प्रदर्शन करती है और डिफ़ॉल्ट पर सुधार हो सकता है के साथ हमें प्रदान करता है __repr__वर्गों में से:

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

तो अब जब हम इस मेटाक्लास के साथ ऑब्जेक्ट बनाते हैं, __repr__तो कमांड लाइन पर प्रतिबिंबित डिफ़ॉल्ट से बहुत कम बदसूरत दृष्टि प्रदान करता है:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

__repr__कक्षा के उदाहरण के लिए एक अच्छी परिभाषा के साथ , हमारे पास हमारे कोड को डीबग करने की एक मजबूत क्षमता है। हालांकि, इसके साथ बहुत अधिक जांच eval(repr(Class))की संभावना नहीं है (क्योंकि कार्यों को उनके डिफ़ॉल्ट से निकालना असंभव होगा __repr__)।

एक अपेक्षित उपयोग: __prepare__एक नामस्थान

यदि, उदाहरण के लिए, हम जानना चाहते हैं कि कक्षा के तरीकों का किस क्रम में बनाया गया है, हम कक्षा के नामस्थान के रूप में आदेशित आदेश प्रदान कर सकते हैं। हम ऐसा करेंगे __prepare__जो कक्षा के लिए नेमस्पेस निर्देश देता है यदि इसे पायथन 3 में लागू किया गया है :

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

और उपयोग:

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

और अब हमारे पास उस क्रम का रिकॉर्ड है जिसमें इन विधियों (और अन्य वर्ग विशेषताओं) बनाए गए थे:

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

नोट, यह उदाहरण docs से अनुकूलित किया गया था - मानक पुस्तकालय में नया enum यह करता है।

तो हमने एक वर्ग बनाकर एक मेटाक्लास को तत्काल किया था। हम मेटाक्लास का भी इलाज कर सकते हैं क्योंकि हम किसी अन्य वर्ग के साथ करेंगे। इसमें एक विधि संकल्प आदेश है:

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

और यह लगभग सही है repr(जिसे हम अब तक नहीं निकाल सकते हैं जब तक कि हम अपने कार्यों का प्रतिनिधित्व करने का कोई तरीका नहीं ढूंढ सकें।):

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



yieldएक समारोह के लिए एक वापसी तत्व की तरह है। अंतर यह है कि yieldतत्व एक जेनरेटर में एक फ़ंक्शन बदल देता है। एक जनरेटर एक समारोह की तरह व्यवहार करता है जब तक कुछ 'उपज' नहीं होता है। जनरेटर तब तक बंद हो जाता है जब तक इसे अगली बार बुलाया न जाए, और उसी बिंदु से जारी रहता है जैसा कि यह शुरू हुआ था। आप कॉल करके, सभी में 'उपज' मूल्यों का अनुक्रम प्राप्त कर सकते हैं list(generator())





python oop metaclass python-datamodel