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




7 Answers

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

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

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

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

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

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

सबसे पहले, आप कक्षा का उपयोग कर फ़ंक्शन में कक्षा बना सकते हैं:

>>> 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__ विशेषता

पायथन 2 में, जब आप कक्षा लिखते हैं तो आप __metaclass__ विशेषता जोड़ सकते हैं (पायथन 3 वाक्यविन्यास के लिए अगला अनुभाग देखें):

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

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

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

आप class Foo(object) पहले लिखते हैं, लेकिन क्लास ऑब्जेक्ट Foo अभी तक स्मृति में नहीं बनाया गया है।

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

कई बार पढ़ें।

जब तुम करोगे:

class Foo(Bar):
    pass

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

क्या Foo में __metaclass__ विशेषता है?

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

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

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

यहां सावधान रहें कि __metaclass__ विशेषता विरासत में नहीं मिलेगी, माता-पिता का Bar.__class__ ( Bar.__class__ ) होगा। यदि Bar ने __metaclass__ विशेषता का उपयोग किया जो Bar type() type.__new__() नहीं करता है (और type.__new__() नहीं करता है type.__new__() ), उप-वर्ग उस व्यवहार का उत्तराधिकारी नहीं होंगे।

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

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

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

पायथन 3 में मेटाक्लास

मेटाक्लास सेट करने के लिए वाक्यविन्यास पाइथन 3 में बदल दिया गया है:

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

यानी बेस वर्गों की सूची में एक कीवर्ड तर्क के पक्ष में __metaclass__ विशेषता का अब उपयोग नहीं किया जाता है।

मेटाक्लास के व्यवहार हालांकि काफी हद तक रहता है।

कस्टम metaclasses

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

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

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

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

सौभाग्य से, __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__ , vars।

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

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

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

चूंकि __metaclass__ किसी भी __metaclass__ करने __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 वापस करेगा, और इसे सीधे डेटाबेस से भी ले जा सकते हैं।

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

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

आख़िरी शब्द

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

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

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

सब कुछ पाइथन में एक वस्तु है, और वे या तो वर्गों या मेटाक्लास के उदाहरणों के उदाहरण हैं।

type अलावा।

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

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

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

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

python oop metaclass python-datamodel

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




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

उदाहरण के लिए, यदि आप 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%।




पायथन 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



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

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

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()



टीएल; डॉ संस्करण

type(obj)समारोह आप किसी ऑब्जेक्ट के प्रकार के हो जाता है।

type()एक वर्ग की अपनी है metaclass

मेटाक्लास का उपयोग करने के लिए:

class Foo(object):
    __metaclass__ = MyMetaClass



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

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

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




Related