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


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


4584
2017-09-19 06:10


मूल




जवाब:


एक मेटाक्लास एक वर्ग की कक्षा है। एक वर्ग की तरह परिभाषित करता है कि वर्ग का एक उदाहरण कैसा व्यवहार करता है, एक मेटाक्लास परिभाषित करता है कि कक्षा कैसे व्यवहार करती है। एक वर्ग मेटाक्लास का एक उदाहरण है।

metaclass diagram

पाइथन में जबकि आप metaclasses के लिए मनमाने ढंग से कॉलबेल का उपयोग कर सकते हैं (जैसे Jerub शो), अधिक उपयोगी दृष्टिकोण वास्तव में इसे एक वास्तविक वर्ग बनाने के लिए है। type पायथन में सामान्य मेटाक्लास है। यदि आप सोच रहे हैं, हाँ, type खुद ही एक वर्ग है, और यह स्वयं का प्रकार है। आप कुछ ऐसा करने में सक्षम नहीं होंगे type पूरी तरह से पायथन में, लेकिन पाइथन थोड़ा धोखा देती है। पायथन में अपना खुद का मेटाक्लास बनाने के लिए आप वास्तव में बस उपclass करना चाहते हैं type

क्लास फैक्ट्री के रूप में आमतौर पर मेटाक्लास का उपयोग किया जाता है। जैसा कि आप वर्ग को कॉल करके कक्षा का एक उदाहरण बनाते हैं, पाइथन मेटाक्लास को कॉल करके एक नई कक्षा (जब यह 'वर्ग' कथन निष्पादित करता है) बनाता है। सामान्य के साथ संयुक्त __init__ तथा __new__ विधियों, मेटाक्लास इसलिए आपको कक्षा बनाने के दौरान 'अतिरिक्त चीजें' करने की अनुमति देता है, जैसे कुछ रजिस्ट्री के साथ नई कक्षा को पंजीकृत करना, या कक्षा को पूरी तरह से किसी अन्य चीज़ से प्रतिस्थापित करना।

जब class कथन निष्पादित किया जाता है, पायथन पहले शरीर का निष्पादन करता है class कोड के सामान्य ब्लॉक के रूप में बयान। परिणामी नामस्थान (एक dict) कक्षा-से-गुण के गुण रखता है। मेटाक्लास क्लास-टू-बी (मेटाक्लास विरासत में प्राप्त) के बेसक्लास को देखकर निर्धारित किया जाता है, पर __metaclass__ क्लास-टू-बी (यदि कोई हो) या की विशेषता __metaclass__ वैश्विक चर। इसके बाद मेटाक्लास को कक्षा के नाम, आधार और गुणों के साथ बुलाया जाता है ताकि इसे तुरंत चालू किया जा सके।

हालांकि, मेटाक्लास वास्तव में परिभाषित करते हैं प्रकार एक वर्ग के लिए, न केवल इसके लिए एक कारखाना, ताकि आप उनके साथ और अधिक कर सकें। उदाहरण के लिए, आप मेटाक्लास पर सामान्य तरीकों को परिभाषित कर सकते हैं। ये मेटाक्लास-विधियां क्लासमेड की तरह हैं, जिसमें उन्हें बिना किसी उदाहरण के कक्षा में बुलाया जा सकता है, लेकिन वे कक्षा के तरीकों की तरह भी नहीं हैं, जिन्हें कक्षा के उदाहरण पर नहीं कहा जा सकता है। type.__subclasses__() पर एक विधि का एक उदाहरण है type metaclass। आप सामान्य 'जादू' विधियों को भी परिभाषित कर सकते हैं, जैसे __add__, __iter__ तथा __getattr__कक्षा को व्यवहार करने के तरीके को लागू या बदलने के लिए।

बिट्स और टुकड़ों का एक समेकित उदाहरण यहां दिया गया है:

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__

2087
2017-09-19 07:01



"आप पाइथन में पूरी तरह से टाइप करने की तरह कुछ नहीं कर पाएंगे, लेकिन पाइथन थोड़ा धोखा देती है" सच नहीं है। - ppperry
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b - ppperry


वस्तुओं के रूप में वर्ग

मेटाक्लास को समझने से पहले, आपको पायथन में कक्षाएं मास्टर करने की आवश्यकता है। और पाइथन के पास क्लासिकल भाषा से उधार लेने वाले वर्गों का एक बहुत ही असाधारण विचार है।

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

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

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

लेकिन पाइथन में कक्षाएं उससे अधिक हैं। कक्षाएं वस्तुएं भी हैं।

हाँ, वस्तुओं।

जैसे ही आप कीवर्ड का उपयोग करते हैं class, पायथन इसे निष्पादित करता है और बनाता है एक वस्तु। निर्देष

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

मेमोरी में "ऑब्जेक्ट क्रिएटर" नाम के साथ एक ऑब्जेक्ट बनाता है।

यह वस्तु (कक्षा) स्वयं वस्तुओं (उदाहरण) बनाने में सक्षम है, और यही कारण है कि यह एक वर्ग है

लेकिन फिर भी, यह एक वस्तु है, और इसलिए:

  • आप इसे एक चर के लिए असाइन कर सकते हैं
  • आप इसे कॉपी कर सकते हैं
  • आप इसमें विशेषताओं को जोड़ सकते हैं
  • आप इसे फ़ंक्शन पैरामीटर के रूप में पास कर सकते हैं

उदा .:

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

गतिशील रूप से कक्षाएं बनाना

चूंकि कक्षाएं वस्तुएं हैं, इसलिए आप उन्हें किसी भी वस्तु की तरह फ्लाई पर बना सकते हैं।

सबसे पहले, आप फ़ंक्शन में एक वर्ग बना सकते हैं class:

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

लेकिन यह इतना गतिशील नहीं है, क्योंकि आपको अभी भी पूरी कक्षा खुद लिखनी है।

चूंकि कक्षाएं वस्तुएं हैं, इसलिए उन्हें कुछ द्वारा उत्पन्न किया जाना चाहिए।

जब आप इसका उपयोग करते हैं class कीवर्ड, पायथन स्वचालित रूप से इस ऑब्जेक्ट को बनाता है। लेकिन जैसे पायथन में ज्यादातर चीजों के साथ, यह आपको मैन्युअल रूप से करने का एक तरीका देता है।

समारोह याद रखें type? अच्छा पुराना फ़ंक्शन जो आपको बताता है कि क्या एक वस्तु टाइप करें:

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

कुंआ, type एक पूरी तरह से अलग क्षमता है, यह भी फ्लाई पर कक्षाएं बना सकते हैं। type एक वर्ग के विवरण पैरामीटर के रूप में ले सकते हैं, और एक कक्षा वापस।

(मुझे पता है, यह मूर्खतापूर्ण है कि आपके द्वारा पारित पैरामीटर के अनुसार एक ही फ़ंक्शन में दो पूरी तरह से अलग-अलग उपयोग हो सकते हैं। यह पीछे की ओर एक समस्या है पायथन में संगतता)

type इस तरह से काम करता है:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

उदा .:

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

मैन्युअल रूप से इस तरह बनाया जा सकता है:

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

आप देखेंगे कि हम कक्षा के नाम के रूप में "MyShinyClass" का उपयोग करते हैं और चर के रूप में कक्षा संदर्भ पकड़ने के लिए। वे अलग हो सकते हैं, लेकिन चीजों को जटिल करने का कोई कारण नहीं है।

type कक्षा के गुणों को परिभाषित करने के लिए एक शब्दकोश स्वीकार करता है। इसलिए:

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

इसका अनुवाद किया जा सकता है:

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

और एक सामान्य वर्ग के रूप में प्रयोग किया जाता है:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

और निश्चित रूप से, आप इससे प्राप्त कर सकते हैं, इसलिए:

>>>   class FooChild(Foo):
...         pass

होने वाला:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

आखिरकार आप अपनी कक्षा में विधियां जोड़ना चाहेंगे। बस एक समारोह को परिभाषित करें उचित हस्ताक्षर के साथ और इसे एक विशेषता के रूप में असाइन करें।

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

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

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

आप देखते हैं कि हम कहां जा रहे हैं: पायथन में, कक्षाएं वस्तुएं हैं, और आप गतिशील रूप से फ्लाई पर एक कक्षा बना सकते हैं।

जब आप कीवर्ड का उपयोग करते हैं तो यह पाइथन करता है class, और यह मेटाक्लास का उपयोग करके ऐसा करता है।

मेटाक्लास (अंत में) क्या हैं

Metaclasses 'सामान' हैं जो वर्ग बनाता है।

वस्तुओं को बनाने के लिए आप कक्षाओं को परिभाषित करते हैं, है ना?

लेकिन हमने सीखा कि पायथन कक्षाएं वस्तुएं हैं।

खैर, मेटाक्लास वे वस्तुएं बनाते हैं। वे कक्षाओं के वर्ग हैं, आप उन्हें इस तरह से चित्रित कर सकते हैं:

MyClass = MetaClass()
my_object = MyClass()

आपने इसे देखा है type आपको ऐसा कुछ करने देता है:

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

ऐसा इसलिए है क्योंकि समारोह type वास्तव में एक मेटाक्लास है। type है मेटाक्लास पाइथन दृश्यों के पीछे सभी वर्गों को बनाने के लिए उपयोग करता है।

अब आप सोचते हैं कि यह बिल्ली लोअरकेस में क्यों लिखा गया है, और नहीं Type?

खैर, मुझे लगता है कि यह स्थिरता का मामला है str, वर्ग जो बनाता है स्ट्रिंग ऑब्जेक्ट्स, और intकक्षा जो पूर्णांक वस्तुओं को बनाता है। type है केवल वर्ग जो वर्ग वस्तुओं को बनाता है।

आप देखते हैं कि जांच कर __class__ विशेषता।

सबकुछ, और मेरा मतलब सबकुछ है, पाइथन में एक वस्तु है। इसमें इन्स शामिल हैं, तार, कार्य और कक्षाएं। वे सभी वस्तुएं हैं। और उनमें से सभी हैं एक वर्ग से बनाया गया है:

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

अब, क्या है __class__ किसी के भी __class__ ?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

तो, एक मेटाक्लास केवल सामान है जो कक्षा वस्तुओं को बनाता है।

यदि आप चाहें तो आप इसे 'क्लास फैक्ट्री' कह सकते हैं।

type अंतर्निहित मेटाक्लास पाइथन उपयोग करता है, लेकिन निश्चित रूप से, आप अपना बना सकते हैं अपने मेटाक्लास।

__metaclass__ गुण

आप एक जोड़ सकते हैं __metaclass__ जब आप कक्षा लिखते हैं तो विशेषता:

class Foo(object):
    __metaclass__ = something...
    [...]

यदि आप ऐसा करते हैं, तो पाइथन वर्ग बनाने के लिए मेटाक्लास का उपयोग करेगा Foo

सावधान, यह मुश्किल है।

तुम लिखो class Foo(object) पहला, लेकिन वर्ग वस्तु Foo बनाया नहीं गया है स्मृति में अभी तक।

पाइथन की तलाश होगी __metaclass__ कक्षा परिभाषा में। अगर यह पाता है, यह ऑब्जेक्ट क्लास बनाने के लिए इसका इस्तेमाल करेगा Foo। यदि ऐसा नहीं होता है, तो इसका उपयोग होगा type कक्षा बनाने के लिए।

कई बार पढ़ें।

जब तुम करोगे:

class Foo(Bar):
    pass

पायथन निम्नलिखित करता है:

वहां एक __metaclass__ में विशेषता Foo?

यदि हां, तो नाम के साथ मेमोरी में क्लास ऑब्जेक्ट बनाएं (मैंने क्लास ऑब्जेक्ट कहा है, मेरे साथ यहां रहें) Foo क्या उपयोग कर रहा है __metaclass__

अगर पाइथन नहीं मिल पा रहा है __metaclass__, यह एक के लिए देखेगा __metaclass__ मॉड्यूल स्तर पर, और ऐसा करने का प्रयास करें (लेकिन केवल उन वर्गों के लिए जो कुछ भी प्राप्त नहीं करते हैं, मूल रूप से पुराने शैली के वर्ग)।

फिर अगर यह कोई नहीं मिल रहा है __metaclass__ बिल्कुल, यह उपयोग करेगा Bar(पहला अभिभावक) स्वयं मेटाक्लास (जो डिफ़ॉल्ट हो सकता है type) वर्ग वस्तु बनाने के लिए।

यहां सावधान रहें __metaclass__ विशेषता विरासत में नहीं मिलेगी, माता-पिता का मेटाक्लास (Bar.__class__) होगा। अगर Bar एक इस्तेमाल किया __metaclass__ बनाई गई विशेषता Bar साथ में type() (और नहीं type.__new__()), उप-वर्ग उस व्यवहार का उत्तराधिकारी नहीं होंगे।

अब बड़ा सवाल यह है कि आप क्या डाल सकते हैं __metaclass__ ?

जवाब यह है: कुछ ऐसा जो कक्षा बना सकता है।

और कक्षा क्या बना सकती है? type, या कुछ भी जो subclasses या इसका उपयोग करता है।

कस्टम metaclasses

मेटाक्लास का मुख्य उद्देश्य कक्षा को स्वचालित रूप से बदलना है, जब यह बनाया गया है।

आप आमतौर पर एपीआई के लिए ऐसा करते हैं, जहां आप मिलान करने वाली कक्षाएं बनाना चाहते हैं वर्तमान संदर्भ

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

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

किस्मत से, __metaclass__ वास्तव में कोई कॉल करने योग्य हो सकता है, इसे एक होने की आवश्यकता नहीं है औपचारिक वर्ग (मुझे पता है, इसके नाम पर 'कक्षा' के साथ कुछ होने की आवश्यकता नहीं है एक वर्ग, आंकड़े जाओ ... लेकिन यह सहायक है)।

तो हम एक समारोह का उपयोग करके, एक साधारण उदाहरण के साथ शुरू करेंगे।

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

अब, चलो बिल्कुल वही करते हैं, लेकिन एक मेटाक्लास के लिए वास्तविक कक्षा का उपयोग करना:

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

लेकिन यह वास्तव में ओओपी नहीं है। हम फोन करते हैं type सीधे और हम ओवरराइड नहीं करते हैं या माता पिता को बुलाओ __new__। हो जाए:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

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

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

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)

हम इसका उपयोग करके भी क्लीनर बना सकते हैं super, जो विरासत को कम करेगा (क्योंकि हां, आप मेटाक्लास, मेटाक्लास से उत्तराधिकारी, प्रकार से विरासत प्राप्त कर सकते हैं):

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)

बस। Metaclasses के बारे में वास्तव में और कुछ भी नहीं है।

मेटाक्लास का उपयोग कर कोड की जटिलता के कारण कारण नहीं है मेटाक्लास के कारण, ऐसा इसलिए होता है क्योंकि आप आमतौर पर मोड़ वाली चीजें करने के लिए मेटाक्लास का उपयोग करते हैं आत्मनिरीक्षण पर निर्भर, विरासत में छेड़छाड़, जैसे युद्ध __dict__, आदि।

दरअसल, मेटाक्लास विशेष रूप से काले जादू करने के लिए उपयोगी होते हैं, और इसलिए जटिल सामान लेकिन खुद से, वे सरल हैं:

  • कक्षा निर्माण को रोकें
  • कक्षा को संशोधित करें
  • संशोधित कक्षा वापस करें

आप कार्यों के बजाय मेटाक्लास वर्गों का उपयोग क्यों करेंगे?

जबसे __metaclass__ किसी भी कॉल करने योग्य को स्वीकार कर सकते हैं, आप कक्षा का उपयोग क्यों करेंगे चूंकि यह स्पष्ट रूप से अधिक जटिल है?

ऐसा करने के कई कारण हैं:

  • इरादा स्पष्ट है। जब आप पढ़ते हैं UpperAttrMetaclass(type), तुम्हे पता हैं क्या चल रहा है
  • आप ओओपी का उपयोग कर सकते हैं। Metaclass मेटाक्लास से उत्तराधिकारी हो सकता है, पैरेंट विधियों को ओवरराइड कर सकता है। मेटाक्लास भी मेटाक्लास का उपयोग कर सकते हैं।
  • यदि आप मेटाक्लास-क्लास निर्दिष्ट करते हैं, लेकिन मेटाक्लास-फ़ंक्शन के साथ नहीं, तो कक्षा के उप-वर्ग इसके मेटाक्लास के उदाहरण होंगे।
  • आप अपने कोड को बेहतर बना सकते हैं। आप किसी चीज़ के लिए मेटाक्लास का कभी भी उपयोग नहीं करते हैं उपर्युक्त उदाहरण के रूप में तुच्छ। यह आमतौर पर कुछ जटिल के लिए है। होने के बाद कई तरीकों को बनाने और उन्हें एक वर्ग में समूह बनाने की क्षमता बहुत उपयोगी है कोड को पढ़ने में आसान बनाने के लिए।
  • आप हुक कर सकते हैं __new__, __init__ तथा __call__। जो अनुमति देगा आप अलग-अलग सामान करने के लिए। यहां तक ​​कि यदि आमतौर पर आप इसे सब कुछ कर सकते हैं __new__, कुछ लोग इसका उपयोग कर अधिक आरामदायक हैं __init__
  • इन्हें मेटाक्लास कहा जाता है, अरे! इसका मतलब कुछ होना चाहिए!

आप मेटाक्लास का उपयोग क्यों करेंगे?

अब बड़ा सवाल है। आप कुछ अस्पष्ट त्रुटि प्रवण सुविधा का उपयोग क्यों करेंगे?

खैर, आमतौर पर आप नहीं करते हैं:

Metaclasses गहरे जादू हैं कि   99% उपयोगकर्ताओं को कभी चिंता नहीं करनी चाहिए।   यदि आपको आश्चर्य है कि आपको उनकी आवश्यकता है या नहीं,   आप नहीं (वास्तव में लोग जो   उन्हें निश्चित रूप से पता होना चाहिए कि   उन्हें उनकी ज़रूरत है, और इसकी आवश्यकता नहीं है   क्यों के बारे में स्पष्टीकरण)।

पायथन गुरु टिम पीटर्स

मेटाक्लास के लिए मुख्य उपयोग केस एक एपीआई बना रहा है। इसका एक सामान्य उदाहरण Django ORM है।

यह आपको इस तरह कुछ परिभाषित करने की अनुमति देता है:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

लेकिन अगर आप ऐसा करते हैं:

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

यह एक वापस नहीं करेगा IntegerField वस्तु। यह एक वापस आ जाएगा int, और इसे सीधे डेटाबेस से भी ले जा सकते हैं।

यह संभव है क्योंकि models.Model परिभाषित करता है __metaclass__ तथा यह कुछ जादू का उपयोग करता है जो चालू हो जाएगा Person आपने बस सरल कथन के साथ परिभाषित किया है एक डेटाबेस क्षेत्र में एक जटिल हुक में।

डीजेंगो एक साधारण एपीआई को उजागर करके कुछ जटिल दिखता है और मेटाक्लास का उपयोग करके, असली एपीआई करने के लिए इस एपीआई से कोड को दोबारा बनाना परदे के पीछे।

आख़िरी शब्द

सबसे पहले, आप जानते हैं कि कक्षाएं ऐसी वस्तुएं हैं जो उदाहरण बना सकती हैं।

वास्तव में, कक्षाएं खुद के उदाहरण हैं। Metaclasses के।

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

सब कुछ पायथन में एक वस्तु है, और वे सभी कक्षाओं के उदाहरण हैं या metaclasses के उदाहरण।

के अलावा type

type वास्तव में इसका अपना मेटाक्लास है। यह ऐसा कुछ नहीं है जो आप कर सकते थे शुद्ध पायथन में पुनरुत्पादन, और कार्यान्वयन पर थोड़ा धोखा देकर किया जाता है स्तर।

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

99% बार आपको कक्षा में बदलाव की आवश्यकता है, आप इनका उपयोग करने से बेहतर हैं।

लेकिन 98% समय, आपको कक्षा में बदलाव की आवश्यकता नहीं है।


5763
2017-09-19 06:26



ऐसा लगता है कि Django में models.Model यह उपयोग नहीं करता है __metaclass__ बल्कि class Model(metaclass=ModelBase): संदर्भित करने के लिए ModelBase कक्षा जो उपर्युक्त मेटाक्लास जादू करता है। महान पद! Django स्रोत यहाँ है: github.com/django/django/blob/master/django/db/models/... - Max Goodridge
<< यहां सावधान रहें __metaclass__ विशेषता विरासत में नहीं मिलेगी, माता-पिता का मेटाक्लास (Bar.__class__) होगा। अगर Bar एक इस्तेमाल किया __metaclass__ बनाई गई विशेषता Bar साथ में type() (और नहीं type.__new__()), उप-वर्ग उस व्यवहार का उत्तराधिकारी नहीं होंगे। >> - क्या आप / कृपया इस मार्ग को थोड़ा गहरा समझा सकते हैं? - petrux
@MaxGoodridge यह मेटाक्लास के लिए पाइथन 3 वाक्यविन्यास है। देख पायथन 3.6 डेटा मॉडल वी.एस. पायथन 2.7 डेटा मॉडल - TBBle
यह एक समुदाय विकी उत्तर है (इसलिए, जो सुधार / सुधार के साथ टिप्पणी करते हैं, वे अपने टिप्पणियों को उत्तर में संपादित करने पर विचार कर सकते हैं, अगर वे सुनिश्चित हैं कि वे सही हैं)। - Shule
इस उत्तर के कौन से हिस्से के बारे में है python2 और किसके बारे में pythono3? - styrofoam fly


नोट, यह उत्तर 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 Initalised के एक subclass पर पारित नहीं किया गया है।

यहां एक और अधिक ठोस उदाहरण दिया गया है, यह दर्शाता है कि क्लास बनने पर कार्रवाई करने वाले मेटाक्लास को बनाने के लिए आप 'टाइप' को कैसे उप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

312
2017-09-19 06:45





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

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

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

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


125
2018-06-21 16:30



मेटा वर्गों का उपयोग नई गुणों और तरीकों को जोड़ने के लिए नहीं है कक्षा और एक उदाहरण नहीं? जहां तक ​​मुझे समझ में आया कि मेटा क्लास कक्षा को बदल देती है और नतीजतन, बदले गए वर्ग द्वारा उदाहरणों का निर्माण अलग-अलग किया जा सकता है। मेटा वर्ग की प्रकृति पाने की कोशिश करने वाले लोगों के लिए थोड़ा भ्रामक हो सकता है। उदाहरणों पर उपयोगी विधियां होने से सामान्य सहनशीलता प्राप्त की जा सकती है। उदाहरण के तौर पर Django कोड का संदर्भ अच्छा है, हालांकि। - trixn


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

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


117
2017-09-19 06:32





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

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

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

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

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


86
2017-08-10 23:28





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

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

स्यूडोकोड:

>>> Class(...)
instance

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

>>> Metaclass(...)
Class

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

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

इसे अलग रखो

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

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

    >>> object()                          # instantiation of class
    <object o