एक `क्लासमैथोड` और मेटाक्लास विधि के बीच अंतर क्या हैं?


12

पायथन में, मैं @classmethodडेकोरेटर का उपयोग करके एक वर्ग विधि बना सकता हूं :

>>> class C:
...     @classmethod
...     def f(cls):
...             print(f'f called with cls={cls}')
...
>>> C.f()
f called with cls=<class '__main__.C'>

वैकल्पिक रूप से, मैं मेटाक्लस पर एक सामान्य (उदाहरण) विधि का उपयोग कर सकता हूं:

>>> class M(type):
...     def f(cls):
...             print(f'f called with cls={cls}')
...
>>> class C(metaclass=M):
...     pass
...
>>> C.f()
f called with cls=<class '__main__.C'>

जैसा कि आउटपुट द्वारा दिखाया गया है C.f(), ये दोनों दृष्टिकोण समान कार्यक्षमता प्रदान करते हैं।

@classmethodमेटाक्लास पर एक सामान्य विधि का उपयोग करने और उपयोग करने के बीच अंतर क्या हैं ?

जवाबों:


5

चूंकि कक्षाएं मेटाक्लास के उदाहरण हैं, इसलिए यह अप्रत्याशित नहीं है कि मेटाक्लास पर एक "इंस्टेंस विधि" एक क्लासमेथोड की तरह व्यवहार करेगी।

हालांकि, हां, मतभेद हैं - और उनमें से कुछ शब्दार्थ से अधिक हैं:

  1. सबसे महत्वपूर्ण अंतर यह है कि मेटाक्लास में एक विधि एक वर्ग उदाहरण से "दृश्यमान" नहीं है । ऐसा इसलिए होता है क्योंकि पाइथन में विशेषता का पता लगाना (एक सरलीकृत तरीके से - वर्णनकर्ता पूर्ववर्ती समय ले सकते हैं) उदाहरण में एक विशेषता की खोज करते हैं - यदि यह उदाहरण में मौजूद नहीं है, तो पायथन उस इंस्टेंस की कक्षा में दिखता है, और फिर खोज जारी रहती है वर्ग के सुपरक्लास, लेकिन वर्ग के वर्ग पर नहीं । पायथन stdlib abc.ABCMeta.registerविधि में इस सुविधा का उपयोग करते हैं। उस सुविधा का उपयोग अच्छे के लिए किया जा सकता है, क्योंकि वर्ग से संबंधित तरीके स्वयं को फिर से उपयोग करने के लिए स्वतंत्र हैं, उदाहरण के लिए बिना किसी संघर्ष के विशेषताएँ (लेकिन एक तरीका अभी भी संघर्ष होगा)।
  2. एक और अंतर, हालांकि स्पष्ट है, यह है कि मेटाक्लास में घोषित एक विधि कई वर्गों में उपलब्ध हो सकती है, अन्यथा संबंधित नहीं - यदि आपके पास अलग-अलग श्रेणी के पदानुक्रम हैं, तो वे जो भी व्यवहार करते हैं, उससे संबंधित नहीं हैं , लेकिन सभी वर्गों के लिए कुछ सामान्य कार्यक्षमता चाहते हैं। , आपको एक मिक्सिन वर्ग के साथ आना होगा, जिसे दोनों पदानुक्रमों में आधार के रूप में शामिल करना होगा (एक आवेदन रजिस्ट्री में सभी वर्गों को शामिल करने के लिए)। (एनबी। मिश्रण कभी-कभी मेटाक्लस की तुलना में बेहतर कॉल हो सकता है)
  3. क्लासमैथोड एक विशिष्ट "क्लासमेथोड" वस्तु है, जबकि मेटाक्लास में एक विधि एक साधारण कार्य है।

तो, ऐसा होता है कि क्लासमैथोड का उपयोग करने वाला तंत्र " डिस्क्रिप्टर प्रोटोकॉल " है। हालांकि सामान्य फ़ंक्शन में एक __get__विधि होती है जो selfएक उदाहरण से पुनर्प्राप्त किए जाने पर तर्क को सम्मिलित करेगा , और उस तर्क को खाली छोड़ देगा जब एक वर्ग से पुनर्प्राप्त किया जाता है, एक classmethodवस्तु का एक अलग होता है __get__, जो कक्षा को "मालिक" के रूप में सम्मिलित करेगा। दोनों स्थितियों में पहला पैरामीटर।

यह अधिकतर समय कोई व्यावहारिक अंतर नहीं करता है, लेकिन यदि आप फ़ंक्शन के रूप में विधि तक पहुंच चाहते हैं, तो इसके लिए गतिशील रूप से डेकोरेटर जोड़ने के उद्देश्य से, या किसी अन्य के लिए, मेटाक्लास में एक विधि के meta.methodलिए फ़ंक्शन को पुनर्प्राप्त करता है, जिसका उपयोग करने के लिए तैयार है , जबकि आपको cls.my_classmethod.__func__ इसे एक क्लासमेथोड से पुनर्प्राप्त करने के लिए उपयोग करना है (और फिर आपको एक अन्य classmethodऑब्जेक्ट बनाना होगा और इसे वापस असाइन करना होगा, यदि आप कुछ रैपिंग करते हैं)।

मूल रूप से, ये 2 उदाहरण हैं:


class M1(type):
    def clsmethod1(cls):
        pass

class CLS1(metaclass=M1):
    pass

def runtime_wrap(cls, method_name, wrapper):
    mcls = type(cls)
    setattr(mcls, method_name,  wrapper(getatttr(mcls, method_name)))

def wrapper(classmethod):
    def new_method(cls):
        print("wrapper called")
        return classmethod(cls)
    return new_method

runtime_wrap(cls1, "clsmethod1", wrapper)

class CLS2:
    @classmethod
    def classmethod2(cls):
        pass

 def runtime_wrap2(cls, method_name, wrapper):
    setattr(cls, method_name,  classmethod(
                wrapper(getatttr(cls, method_name).__func__)
        )
    )

runtime_wrap2(cls1, "clsmethod1", wrapper)

दूसरे शब्दों में: महत्वपूर्ण अंतर के अलावा कि मेटाक्लास में परिभाषित एक विधि उदाहरण से दिखाई देती है और एक classmethodवस्तु नहीं है, अन्य अंतर, रनटाइम पर अस्पष्ट और व्यर्थ लगेंगे - लेकिन ऐसा इसलिए होता है क्योंकि भाषा को जाने की आवश्यकता नहीं होती है क्लासमैथोड्स के लिए विशेष नियमों के साथ अपने तरीके से बाहर: एक क्लासमेथोड घोषित करने के दोनों तरीके संभव हैं, परिणामस्वरूप भाषा डिजाइन - एक, इस तथ्य के लिए कि एक वर्ग स्वयं एक वस्तु है, और दूसरा, कई लोगों के बीच एक संभावना के रूप में, डिस्क्रिप्टर प्रोटोकॉल का उपयोग जो किसी को एक उदाहरण में और एक वर्ग में विशेषता का उपयोग करने की अनुमति देता है:

classmethodनिर्मित मूल कोड में परिभाषित किया गया है, लेकिन यह सिर्फ शुद्ध अजगर में कोडित किया जा सकता है और ठीक उसी तरह से काम करेगा। 5 लाइन क्लास के बेलो classmethodको डेकोरेटर के रूप में इस्तेमाल किया जा सकता है, जिसमें बिल्ट-इन- @classmethod" at all (though distinguishable through introspection such as calls toइनस्टीन , and evenरिप्र `कोर्स के लिए कोई रनटाइम अंतर नहीं है :


class myclassmethod:
    def __init__(self, func):
        self.__func__ = func
    def __get__(self, instance, owner):
        return lambda *args, **kw: self.__func__(owner, *args, **kw)

और, तरीकों से परे, यह ध्यान रखना दिलचस्प है कि @propertyमेटाक्लास पर विशेष गुण विशेष वर्ग विशेषताओं के रूप में काम करेंगे, बस एक ही, कोई आश्चर्यजनक व्यवहार नहीं है।


2

जब आप इसे इस तरह वाक्यांश देते हैं जैसे आपने प्रश्न में किया था, @classmethodऔर मेटाक्लस समान दिख सकते हैं, लेकिन उनके अलग-अलग उद्देश्य हैं। वर्ग जो @classmethodतर्क में इंजेक्ट किया जाता है वह आमतौर पर एक उदाहरण (यानी एक वैकल्पिक कंस्ट्रक्टर) के निर्माण के लिए उपयोग किया जाता है। दूसरी ओर, मेटाक्लासेस आमतौर पर कक्षा को संशोधित करने के लिए उपयोग किया जाता है (जैसे कि Django अपने मॉडल DSL के साथ क्या करता है)।

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

आइए पहले उदाहरण का थोड़ा विस्तार करें।

class C:
    @classmethod
    def f(cls):
        print(f'f called with cls={cls}')

पायथन डॉक्स से उधार लेना , उपरोक्त कुछ इस तरह का विस्तार करेगा:

class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        def newfunc(*args):
            return self.f(klass, *args)
        return newfunc

class C:
    def f(cls):
        print(f'f called with cls={cls}')
    f = ClassMethod(f)

ध्यान दें कि कैसे __get__या तो एक उदाहरण या वर्ग (या दोनों) ले सकते हैं, और इस प्रकार आप दोनों C.fऔर कर सकते हैं C().f। यह आपके द्वारा दिए गए मेटाक्लस के उदाहरण के विपरीत है जो एक के AttributeErrorलिए फेंक देगा C().f

इसके अलावा, मेटाक्लस उदाहरण में, fमें मौजूद नहीं है C.__dict__। जब विशेषता fको देखते हैं C.f, तो दुभाषिया दिखता है C.__dict__और फिर खोजने में विफल होने के बाद, type(C).__dict__(जो है M.__dict__) को देखता है । यदि आप लचीलापन ओवरराइड करने के लिए चाहते हैं तो यह कोई फर्क हो सकता है fमें C, हालांकि मुझे शक है यह कभी व्यावहारिक फायदा नहीं होगा।


0

आपके उदाहरण में, अंतर कुछ अन्य वर्गों में होगा जो एम सेट उनके मेटाक्लस के रूप में होंगे।

class M(type):
    def f(cls):
        pass

class C(metaclass=M):
    pass

class C2(metaclass=M):
    pass

C.f()
C2.f()
class M(type):
     pass

class C(metaclass=M):
     @classmethod
     def f(cls):
        pass

class C2(metaclass=M):
    pass

C.f()
# C2 does not have 'f'

यहाँ मेटाक्लासेस पर अधिक है मेटाक्लासेस के लिए कुछ (ठोस) उपयोग-मामले क्या हैं?


0

@Classmethod और Metaclass दोनों अलग-अलग हैं।

अजगर में सब कुछ एक वस्तु है। हर चीज का मतलब हर चीज है।

मेटाक्लस क्या है?

जैसा कि कहा जाता है कि प्रत्येक वस्तु एक वस्तु है। कक्षाएं भी वस्तुएं हैं वास्तव में कक्षाएं अन्य रहस्यमय वस्तुओं के उदाहरण हैं जिन्हें औपचारिक रूप से मेटा-कक्षाएं कहा जाता है। Python में डिफ़ॉल्ट मेटाक्लास "प्रकार" है यदि निर्दिष्ट नहीं है

डिफ़ॉल्ट रूप से परिभाषित सभी वर्ग प्रकार के उदाहरण हैं।

कक्षाएं मेटा-क्लासेस के उदाहरण हैं

कुछ महत्वपूर्ण बिंदुओं को मेटियन व्यवहार को समझना है

  • चूंकि कक्षाएं मेटा क्लासेस के उदाहरण हैं।
  • प्रत्येक तात्कालिक वस्तु की तरह, वस्तुओं (उदाहरणों) को वर्ग से उनके गुण मिलते हैं। वर्ग को यह मेटा-क्लास से विशेषताएँ मिलेंगी

निम्नलिखित कोड पर विचार करें

class Meta(type):
    def foo(self):
        print(f'foo is called self={self}')
        print('{} is instance of {}: {}'.format(self, Meta, isinstance(self, Meta)))

class C(metaclass=Meta):
    pass

C.foo()

कहाँ पे,

  • कक्षा C वर्ग मेटा का उदाहरण है
  • "क्लास सी" क्लास ऑब्जेक्ट है जो "क्लास मेटा" का उदाहरण है
  • किसी भी अन्य ऑब्जेक्ट (उदाहरण) की तरह "क्लास सी" की पहुंच है, यह "क्लास मेटा" में परिभाषित विशेषताओं / विधियों है
  • तो, "C.foo ()" को डिकोड करना। "सी" "मेटा" का उदाहरण है और "फू" "मेटा" के उदाहरण के माध्यम से विधि कॉलिंग है जो "सी" है।
  • विधि "फू" का पहला तर्क उदाहरण के लिए "क्लासमेथोड" के विपरीत वर्ग नहीं है

हम यह सत्यापित कर सकते हैं कि "वर्ग C" "वर्ग मेटा" का उदाहरण है

  isinstance(C, Meta)

क्लासमथोड क्या है?

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

चूंकि क्लास के तरीके क्लास से बंधे होते हैं। यह कम से कम एक तर्क लेता है जो उदाहरण (स्व) के बजाय कक्षा के संदर्भ में है

यदि अंतर्निहित फ़ंक्शन / डेकोरेटर क्लासमेथोड का उपयोग किया जाता है। पहला तर्क उदाहरण के बजाय कक्षा का संदर्भ होगा

class ClassMethodDemo:
    @classmethod
    def foo(cls):
        print(f'cls is ClassMethodDemo: {cls is ClassMethodDemo}')

जैसा कि हमने "क्लासमेथोड" का उपयोग किया है, हम बिना किसी उदाहरण के बनाने के लिए विधि "फू" कहते हैं

ClassMethodDemo.foo()

उपरोक्त विधि कॉल सत्य वापस आ जाएगी। चूँकि पहला तर्क cls वास्तव में "ClassMethodDemo" का संदर्भ है

सारांश:

  • क्लासमेथोड का पहला तर्क है जो "क्लास के लिए एक संदर्भ (पारंपरिक रूप से cls के रूप में संदर्भित) है"
  • मेटा-क्लास के तरीके क्लासमेथोड नहीं हैं। मेटा-क्लास के तरीके पहले तर्क प्राप्त करते हैं जो "उदाहरण के लिए एक संदर्भ है (पारंपरिक रूप से स्वयं के रूप में संदर्भित) वर्ग नहीं"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.