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




oop metaclass (10)

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

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

स्यूडोकोड:

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

https://code.i-harness.com

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


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

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

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

>>> 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% समय, आपको कक्षा में बदलाव की आवश्यकता नहीं है।


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

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

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

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

class Foo(object):
    __metaclass__ = MyMetaClass

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

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

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

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

हालांकि, मेटाक्लास वास्तव में एक वर्ग के प्रकार को परिभाषित करते हैं, न केवल इसके लिए एक कारखाना, ताकि आप उनके साथ और अधिक कर सकें। उदाहरण के लिए, आप मेटाक्लास पर सामान्य तरीकों को परिभाषित कर सकते हैं। ये मेटाक्लास-विधियां क्लासमेड की तरह हैं, जिसमें उन्हें बिना किसी उदाहरण के कक्षा में बुलाया जा सकता है, लेकिन वे कक्षा के तरीकों की तरह भी नहीं हैं, जिन्हें कक्षा के उदाहरण पर नहीं कहा जा सकता है। type.__subclasses__() type मेटाक्लास पर एक विधि का एक उदाहरण है। वर्ग सामान्य व्यवहार को लागू करने या बदलने के लिए आप __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__

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

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


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

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

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

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


नोट, यह उत्तर 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

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

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

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

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

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


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

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

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





python-datamodel