पायथन में क्लास विधि अंतर: बाध्य, अनबाउंड और स्टैटिक


242

निम्नलिखित वर्ग विधियों के बीच अंतर क्या है?

क्या ऐसा है कि एक स्थिर है और दूसरा नहीं है?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()

18
Method_two () परिभाषा के अलावा कोई अंतर अमान्य नहीं है और इसकी कॉल विफल रहती है।
अनातोली टेकटोनिक

14
@techtonik: method_two की परिभाषा में कुछ भी गलत नहीं है! इसे एक गलत / अमान्य युक्ति में कहा जा रहा है, अर्थात एक अतिरिक्त तर्क के साथ।
0xc0de

1
तुम्हारी दोनों उदाहरण विधियां हैं , वर्ग विधियां नहीं। आप परिभाषा पर लागू करके एक वर्ग विधि बनाते हैं @classmethod। पहले पैरामीटर को clsइसके बजाय बुलाया जाना चाहिए selfऔर आपकी कक्षा के उदाहरण के बजाय कक्षा ऑब्जेक्ट प्राप्त करेगा: Test.method_three()और a_test.method_three()समतुल्य हैं।
लुट्ज़ प्रीचेल

आप selfतर्क के बिना एक फ़ंक्शन परिभाषा क्यों बनाना चाहेंगे ? क्या इसके लिए एक मजबूत उपयोग का मामला है?
अल्फा_989

जवाबों:


412

पायथन में, बाध्य और अनबाउंड के बीच अंतर है विधियों के ।

मूल रूप से, एक सदस्य फ़ंक्शन (जैसे method_one), एक बाध्य फ़ंक्शन को कॉल करता है

a_test.method_one()

के लिए अनुवादित है

Test.method_one(a_test)

यानी एक अनबाउंड विधि के लिए एक कॉल। उसके कारण, आपके संस्करण के लिए एक कॉल method_twoविफल हो जाएगी aTypeError

>>> a_test = Test() 
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

आप एक डेकोरेटर का उपयोग करके विधि के व्यवहार को बदल सकते हैं

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

डेकोरेटर अंतर्निहित डिफ़ॉल्ट मेटाक्लास type(एक वर्ग का वर्ग, सीएफ। इस प्रश्न को ) के लिए बाध्य तरीके नहीं बनाने के लिए कहता हैmethod_two

अब, आप स्थैतिक विधि को एक उदाहरण पर या सीधे कक्षा में शामिल कर सकते हैं:

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two

17
मैं इस जवाब का समर्थन करता हूं, यह मेरे लिए बेहतर है। शाबाश टॉरस्टेन :)
freespace

24
अजगर में 3 अनबाउंड तरीकों को पदावनत किया जाता है। इसके बजाय सिर्फ एक फंक्शन है।
बोल्डनिक

@boldnik, आप क्यों कहते हैं कि अनबाउंड विधियां पदावनत हैं? स्थिर तरीके अभी भी दस्तावेज़ में मौजूद हैं: docs.python.org/3/library/functions.html#staticmethod
alp_989

195

पाइथन में विधियाँ एक बहुत ही सरल बात है, एक बार जब आप डिस्क्रिप्टर सिस्टम की मूल बातें समझ गए। निम्नलिखित वर्ग की कल्पना करें:

class C(object):
    def foo(self):
        pass

अब आइए उस वर्ग पर एक नज़र डालते हैं:

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>

जैसा कि आप देख सकते हैं कि क्या आप उस fooविशेषता को एक्सेस करते हैं जिस पर आपको एक अनबाउंड विधि वापस मिलती है, हालांकि क्लास स्टोरेज (तानाशाह) के अंदर एक फ़ंक्शन है। यही कारण है कि? इसका कारण यह है कि आपकी कक्षा का वर्ग उन __getattribute__विवरणों को लागू करता है जो विवरणकर्ताओं को हल करते हैं। जटिल लगता है, लेकिन नहीं है। C.fooउस विशेष मामले में इस कोड के बराबर है:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

ऐसा इसलिए है क्योंकि फ़ंक्शन में एक __get__विधि है जो उन्हें वर्णनकर्ता बनाती है। यदि आपके पास एक वर्ग का उदाहरण है तो यह लगभग समान है, बस वह Noneवर्ग उदाहरण है:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>

अब पायथन ऐसा क्यों करता है? क्योंकि विधि ऑब्जेक्ट किसी फ़ंक्शन के पहले पैरामीटर को वर्ग के उदाहरण से बांधता है। वहीं से सेल्फ आता है। अब कभी-कभी आप नहीं चाहते कि आपकी कक्षा किसी समारोह को एक विधि बनाए, यही वह जगह staticmethodहै जो खेल में आती है:

 class C(object):
  @staticmethod
  def foo():
   pass

staticmethodडेकोरेटर अपने वर्ग और औजार एक डमी लपेटता __get__समारोह के रूप में लिपटे फ़ंक्शन और एक विधि के रूप में नहीं है कि:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

आशा है कि यह बताते हैं।


12
staticmethodडेकोरेटर लपेटता अपनी कक्षा (...) यह वाक्यांश एक सा वर्ग जो लिपटे किया जा रहा है के रूप में भ्रामक वर्ग है की विधि fooहै और वर्ग है, जिसमें fooपरिभाषित किया गया है।
पायोत्र डोब्रोगोस्ट

12

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

वास्तव में इसे काम करने के लिए, आप इसे अपनी कक्षा परिभाषा में जोड़ सकते हैं:

method_two = staticmethod(method_two)

या आप @staticmethod फ़ंक्शन डेकोरेटर का उपयोग कर सकते हैं ।


4
आपका मतलब है "@staticmethod फ़ंक्शन डेकोरेटर सिंटैक्स"।
tzot 11

11
>>> class Class(object):
...     def __init__(self):
...         self.i = 0
...     def instance_method(self):
...         self.i += 1
...         print self.i
...     c = 0
...     @classmethod
...     def class_method(cls):
...         cls.c += 1
...         print cls.c
...     @staticmethod
...     def static_method(s):
...         s += 1
...         print s
... 
>>> a = Class()
>>> a.class_method()
1
>>> Class.class_method()    # The class shares this value across instances
2
>>> a.instance_method()
1
>>> Class.instance_method() # The class cannot use an instance method
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead)
>>> Class.instance_method(a)
2
>>> b = 0
>>> a.static_method(b)
1
>>> a.static_method(a.c) # Static method does not have direct access to 
>>>                      # class or instance properties.
3
>>> Class.c        # a.c above was passed by value and not by reference.
2
>>> a.c
2
>>> a.c = 5        # The connection between the instance
>>> Class.c        # and its class is weak as seen here.
2
>>> Class.class_method()
3
>>> a.c
5

2
Class.instance_method() # The class cannot use an instance methodयह उपयोग कर सकते हैं। बस उदाहरण को मैन्युअल रूप से पास करें:Class.instance_method(a)
warvariuc

@warwaruk यह वहाँ है, लाइन के नीचे की TyeErrorरेखा को देखो।
kzh

हाँ मैंने इसे बाद में देखा। फिर भी, imo, यह कहना सही नहीं है कि 'कक्षा एक इंस्टेंस विधि का उपयोग नहीं कर सकती', क्योंकि आपने इसे नीचे एक पंक्ति में किया था।
वार्वारियुक

@kzh, आपके स्पष्टीकरण के लिए धन्यवाद। जब आप फोन करते हैं a.class_method(), तो लगता है a.cकि यह अपडेट हो गया है 1, इसलिए कॉल को Class.class_method(), Class.cचर को अपडेट किया गया 2। हालाँकि, जब आपने सौंपा a.c=5, Class.cतो अद्यतन क्यों नहीं किया गया 5?
अल्फा_989

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

4

Method_two काम नहीं करेगा क्योंकि आप एक सदस्य फ़ंक्शन को परिभाषित कर रहे हैं, लेकिन यह नहीं बता रहे हैं कि फ़ंक्शन किस का सदस्य है। यदि आप अंतिम पंक्ति पर अमल करेंगे तो आपको मिलेगा:

>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)

यदि आप किसी कक्षा के लिए सदस्य कार्यों को परिभाषित कर रहे हैं तो पहला तर्क हमेशा 'स्व' होना चाहिए।


3

ऊपर अर्मिन रोनाचेर का सटीक स्पष्टीकरण, उनके उत्तरों पर विस्तार करते हुए ताकि मेरे जैसे शुरुआती इसे अच्छी तरह से समझें:

एक वर्ग में परिभाषित विधियों में अंतर, चाहे स्थिर या उदाहरण विधि (वहाँ अभी तक एक और प्रकार है - वर्ग विधि - यहां चर्चा नहीं की गई है इसलिए इसे छोड़ देना), इस तथ्य में निहित है कि क्या वे किसी तरह वर्ग उदाहरण के लिए बाध्य हैं या नहीं। उदाहरण के लिए, यह कहें कि रनटाइम के दौरान विधि को क्लास के उदाहरण का संदर्भ मिलता है या नहीं

class C:
    a = [] 
    def foo(self):
        pass

C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C() 
c # this is the class instance

__dict__क्लास ऑब्जेक्ट की डिक्शनरी प्रॉपर्टी एक क्लास ऑब्जेक्ट के सभी गुणों और तरीकों का संदर्भ रखती है और इस प्रकार

>>> C.__dict__['foo']
<function foo at 0x17d05b0>

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

यदि CPO एक डिस्क्रिप्टर है, तो अजगर व्याख्याकार कॉल करता है __get__() सीपीओ विधि को उस मूल्य तक पहुंचने के लिए ।

यह निर्धारित करने के लिए कि क्या सीपीओ एक डिस्क्रिप्टर है, अजगर व्याख्याकार जाँच करता है कि क्या यह डिस्क्रिप्टर प्रोटोकॉल को लागू करता है। डिस्क्रिप्टर प्रोटोकॉल को लागू करने के लिए 3 तरीकों को लागू करना है

def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)

उदाहरण के लिए

>>> C.__dict__['foo'].__get__(c, C)

कहाँ पे

  • self CPO है (यह सूची, str, फ़ंक्शन आदि का एक उदाहरण हो सकता है) और रनटाइम द्वारा आपूर्ति की जाती है
  • instance उस वर्ग का उदाहरण है जहां इस CPO को परिभाषित किया गया है (ऊपर 'ऑब्जेक्ट' c ') और हमारे द्वारा आपूर्ति की जाने वाली खोज की आवश्यकता है
  • ownerवह वर्ग है जहाँ इस CPO को परिभाषित किया गया है (ऊपर कक्षा वस्तु 'C') और हमारे द्वारा आपूर्ति की जानी चाहिए। हालाँकि ऐसा इसलिए है क्योंकि हम इसे CPO पर बुला रहे हैं। जब हम इसे उदाहरण पर कहते हैं, तो हमें इसकी आपूर्ति करने की आवश्यकता नहीं है क्योंकि रनटाइम इंस्टेंस या इसके वर्ग (बहुरूपता) की आपूर्ति कर सकता है
  • value सीपीओ के लिए इच्छित मूल्य है और हमारे द्वारा आपूर्ति की जानी चाहिए

सभी सीपीओ डिस्क्रिप्टर नहीं हैं। उदाहरण के लिए

>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510> 
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'

इसका कारण यह है कि सूची वर्ग वर्णनकर्ता प्रोटोकॉल को लागू नहीं करता है।

इस प्रकार तर्क स्वयं c.foo(self)की आवश्यकता है क्योंकि इसकी विधि हस्ताक्षर वास्तव में यही हैC.__dict__['foo'].__get__(c, C) (जैसा कि ऊपर बताया गया है, सी की आवश्यकता नहीं है क्योंकि यह पता लगाया जा सकता है या बहुरूपित हो सकता है) और यही कारण है कि यदि आप उस आवश्यक उदाहरण तर्क को पास नहीं करते हैं तो आपको टाइपऑवर मिलता है।

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

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

class C(object):
  @staticmethod
  def foo():
   pass

नए लिपटे सीपीओ में संदर्भ की अनुपस्थिति fooएक त्रुटि फेंकती है और निम्नानुसार सत्यापित की जा सकती है:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

स्थैतिक विधि का उपयोग एक नाम स्थान और कोड में स्थिरता एक से अधिक है (इसे एक वर्ग से बाहर ले जाना और इसे मॉड्यूल आदि में उपलब्ध कराना)।

जब तक संभव हो, उदाहरण के तरीकों के बजाय स्थिर तरीकों को लिखना बेहतर होगा, जब तक कि संभोग करने के लिए आपको तरीकों का उपयोग करने की आवश्यकता न हो (जैसे कि पहुंच आवृत्ति चर, वर्ग चर आदि)। एक कारण वस्तुओं के अवांछित संदर्भ न रखकर कचरा संग्रह को कम करना है।


1

यह एक त्रुटि है।

सबसे पहले, पहली पंक्ति इस तरह होनी चाहिए (राजधानियों से सावधान रहें)

class Test(object):

जब भी आप किसी वर्ग की विधि कहते हैं, तो वह पहले तर्क (इसलिए स्वयं का नाम) के रूप में हो जाता है और method_two यह त्रुटि देता है

>>> a.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)

1

दूसरा काम नहीं करेगा क्योंकि जब आप इसे कहते हैं तो उस अजगर को आंतरिक रूप से पहले तर्क के रूप में a_test उदाहरण के साथ कॉल करने की कोशिश करता है, लेकिन आपका method_two किसी भी तर्क को स्वीकार नहीं करता है, इसलिए यह काम नहीं करेगा, आपको एक रनटाइम मिलेगा त्रुटि। यदि आप एक स्थिर विधि के बराबर चाहते हैं तो आप एक वर्ग विधि का उपयोग कर सकते हैं। जावा या C # जैसी भाषाओं में स्थिर तरीकों की तुलना में पायथन में वर्ग विधियों की बहुत कम आवश्यकता है। सबसे अधिक बार सबसे अच्छा समाधान मॉड्यूल में एक विधि का उपयोग करना है, एक वर्ग परिभाषा के बाहर, वे वर्ग विधियों की तुलना में अधिक कुशलता से काम करते हैं।


यदि मैं किसी फ़ंक्शन को Class Test(object): @staticmethod def method_two(): print(“called method_two”) एक उपयोग के मामले में परिभाषित करता हूं, तो मैं सोच रहा था कि आप फ़ंक्शन को एक वर्ग का हिस्सा बनाना चाहते हैं, लेकिन उपयोगकर्ता सीधे फ़ंक्शन तक नहीं पहुंचना चाहते हैं। तो उदाहरण के method_twoअंदर अन्य कार्यों द्वारा बुलाया जा सकता है Test, लेकिन कठबोली का उपयोग करके बुलाया जा सकता है a_test.method_two()। यदि मैं उपयोग करता हूं def method_two(), तो क्या यह इस उपयोग के मामले में काम करेगा? या क्या फ़ंक्शन की परिभाषा को संशोधित करने का एक बेहतर तरीका है, इसलिए यह उपरोक्त उपयोग के मामले के लिए काम करता है?
अल्फा_989

1

Method_two को कॉल स्वयं पैरामीटर को स्वीकार नहीं करने के लिए एक अपवाद को फेंक देगा पायथन रनटाइम स्वचालित रूप से इसे पारित करेगा।

यदि आप पायथन क्लास में स्टैटिक मेथड बनाना चाहते हैं, तो इसे सजाएँ staticmethod decorator

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()


0

की परिभाषा method_twoअमान्य है। जब आप कॉल करते हैं method_two, तो आप TypeError: method_two() takes 0 positional arguments but 1 was givenदुभाषिया से प्राप्त करेंगे ।

जब आप इसे कहते हैं तो एक आवृत्ति विधि एक बंधी हुई क्रिया है a_test.method_two()। यह स्वचालित रूप से स्वीकार करता है self, जो Testइसके पहले पैरामीटर के रूप में , उदाहरण के लिए इंगित करता है । selfपैरामीटर के माध्यम से , एक आवृत्ति विधि स्वतंत्र रूप से विशेषताओं तक पहुंच सकती है और उन्हें उसी वस्तु पर संशोधित कर सकती है।


0

अनबाउंड तरीके

अनबाउंड विधियाँ वे विधियाँ हैं जो किसी वर्ग विशेष के लिए अभी तक बाध्य नहीं हैं।

बंधे हुए तरीके

बाउंड विधियां वे हैं जो एक वर्ग के एक विशिष्ट उदाहरण के लिए बाध्य हैं।

जैसा कि यहाँ प्रलेखित है , स्व अलग-अलग चीजों को संदर्भित कर सकता है जो फ़ंक्शन के आधार पर बाध्य, अनबाउंड या स्थिर है।

निम्नलिखित उदाहरण पर एक नज़र डालें:

class MyClass:    
    def some_method(self):
        return self  # For the sake of the example

>>> MyClass().some_method()
<__main__.MyClass object at 0x10e8e43a0># This can also be written as:>>> obj = MyClass()

>>> obj.some_method()
<__main__.MyClass object at 0x10ea12bb0>

# Bound method call:
>>> obj.some_method(10)
TypeError: some_method() takes 1 positional argument but 2 were given

# WHY IT DIDN'T WORK?
# obj.some_method(10) bound call translated as
# MyClass.some_method(obj, 10) unbound method and it takes 2 
# arguments now instead of 1 

# ----- USING THE UNBOUND METHOD ------
>>> MyClass.some_method(10)
10

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

यदि ऐसा है, तो MyClass.some_method(10)कॉल और कॉल के बीच अंतर क्या है जिसे स्टैटिक फंक्शन से सजाया गया है@staticmethod डेकोरेटर के ?

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

इसके अलावा, @staticmethodडेकोरेटर को जोड़कर , हम एक वस्तु के माध्यम से भी पहुंचना संभव बना रहे हैं।

class MyClass:    
    def some_method(self):
        return self    

    @staticmethod
    def some_static_method(number):
        return number

>>> MyClass.some_static_method(10)   # without an instance
10
>>> MyClass().some_static_method(10)   # Calling through an instance
10

आप उदाहरण के तरीकों के साथ उपरोक्त उदाहरण नहीं कर सकते। आप पहले वाले से बच सकते हैं (जैसा कि हमने पहले किया था) लेकिन दूसरे को अनबाउंड कॉल में अनुवादित किया MyClass.some_method(obj, 10)जाएगा, जो तब से उठाएगा TypeErrorजब एक विधि एक तर्क लेती है और आपने अनजाने में दो पास करने की कोशिश की।

तो, आप कह सकते हैं, "अगर मैं एक उदाहरण और एक वर्ग दोनों के माध्यम से स्थिर तरीकों को कॉल कर सकता हूं, MyClass.some_static_methodऔर MyClass().some_static_methodसमान तरीके होने चाहिए"। हाँ!


0

बद्ध विधि = उदाहरण विधि

अनबाउंड विधि = स्थिर विधि।


कृपया अपने उत्तर में कुछ और स्पष्टीकरण जोड़ें, ताकि अन्य इससे सीख सकें। उदाहरण के लिए, इस प्रश्न के अन्य उत्तरों पर एक नज़र डालें
निको हसे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.