2008-09-22 12 views
210

निम्न श्रेणी विधियों के बीच क्या अंतर है?पायथन में क्लास विधि अंतर: बाध्य, अनबाउंड और स्थैतिक

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

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() 
+15

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

+13

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

+1

आपका दोनों ** उदाहरण विधियां ** हैं, क्लास विधियों नहीं। परिभाषा में '@ classmethod' लागू करके आप एक [क्लास विधि] (https://docs.python.org/3/library/functions.html?highlight=classmethod#classmethod) बनाते हैं। पहले पैरामीटर को 'स्वयं' के बजाय 'cls' कहा जाना चाहिए और आपकी कक्षा के उदाहरण के बजाय कक्षा वस्तु प्राप्त होगी:' Test.method_three() 'और' a_test.method_three() 'समकक्ष हैं। –

उत्तर

371

अजगर में, वहाँ बाध्य और अनबाउंड तरीकों के बीच एक अंतर है।

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

a_test.method_one() 

Test.method_one(a_test) 

अर्थात एक अबाध विधि के लिए एक कॉल करने के लिए अनुवाद किया है। इस कारण से, method_two के अपने संस्करण के लिए एक कॉल एक TypeError

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

डेकोरेटर का उपयोग कर एक विधि के व्यवहार को बदल सकते हैं बताता है निर्मित डिफ़ॉल्ट metaclass type (कक्षा की कक्षा, सीएफ this question) 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 
+13

मैं इस उत्तर का समर्थन करता हूं, यह मेरे लिए बेहतर है। अच्छी तरह से किया Torsten :) – freespace

+0

सजावट पर अधिक पढ़ना: http://www.python.org/dev/peps/pep-0318/ – Kieveli

+21

पायथन 3 अनबाउंड विधियों को हटा दिया गया है। इसके बजाय बस एक समारोह है। – boldnik

11

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

वास्तव में यह काम करने के लिए, यदि आप अपने वर्ग परिभाषा को यह संलग्न कर सकते हैं:

method_two = staticmethod(method_two) 

या आप @staticmethodfunction decorator इस्तेमाल कर सकते हैं।

+4

आपका मतलब है "@staticmethod फ़ंक्शन सजावटी वाक्यविन्यास"। – tzot

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) 

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

1

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

यदि आप पाइथन कक्षा में एक स्थिर विधि बनाना चाहते हैं, तो इसे staticmethod decorator से सजाने के लिए।

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

Test.method_two() 
+0

यह या तो काम नहीं करेगा, आपको स्वयं को छोड़ना होगा। –

+0

आप सही हैं। कॉपी-पेस्ट के साथ बहुत जल्दी था। ठीक कर दिया। – MvdD

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 किसी भी तर्क को स्वीकार नहीं करता, इसलिए यह काम नहीं करेगा, आपको रनटाइम त्रुटि मिल जाएगी। यदि आप एक स्थिर विधि के बराबर चाहते हैं तो आप कक्षा विधि का उपयोग कर सकते हैं। जावा या सी # जैसी भाषाओं में स्थिर तरीकों की तुलना में पाइथन में कक्षा विधियों की बहुत कम आवश्यकता है। क्लास परिभाषा के बाहर मॉड्यूल में एक विधि का उपयोग करना सबसे अच्छा समाधान है, जो वर्ग विधियों की तुलना में अधिक कुशलतापूर्वक काम करते हैं।

+0

: स्थिर तरीकों अभी भी दस्तावेज में मौजूद हैं method_two(): \t \t प्रिंट ("कहा जाता है method_two") ' एक प्रयोग के मामले में, मैं के बारे में सोच रहा था है जब आप समारोह एक वर्ग का एक हिस्सा बनना चाहते हैं, लेकिन करते समारोह का उपयोग करने के उपयोगकर्ता नहीं चाहता सीधे। तो 'test_two' को 'टेस्ट' उदाहरण के अंदर अन्य कार्यों द्वारा बुलाया जा सकता है, लेकिन' a_test.method_two() 'का उपयोग करके कॉल नहीं किया जा सकता है। यदि मैं 'def method_two()' का उपयोग करता हूं, तो क्या यह इस उपयोग के मामले के लिए काम करेगा? या क्या फ़ंक्शन परिभाषा को संशोधित करने का एक बेहतर तरीका है, इसलिए यह उपरोक्त उपयोग के मामले के लिए कार्य करता है? –

180

वर्णनकर्ता प्रणाली की मूल बातें समझने के बाद पायथन में तरीके बहुत ही सरल चीज हैं।

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

अब खोल में उस वर्ग पर एक नजर डालते हैं:

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

आप वर्ग पर foo विशेषता का उपयोग करता है, तो आप देख सकते हैं तुम वापस एक अबाध विधि मिल निम्नलिखित वर्ग की कल्पना हालांकि, कक्षा भंडारण (dict) के अंदर एक समारोह है। यही कारण है कि? इसका कारण यह है कि आपकी कक्षा का वर्ग __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>> 

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

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

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

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

आशा है कि यह समझाएगा।

+10

* 'staticmethod' सजावटी आपकी कक्षा को लपेटता है (...) * यह वाक्यांश थोड़ा भ्रामक है क्योंकि जिस वर्ग को लपेटा जा रहा है वह ** ** विधि 'की विधि ** है, जिस वर्ग में' foo ' 'परिभाषित किया गया है। –

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() # कक्षा एक उदाहरण विधि का उपयोग नहीं कर सकती है जिसका उपयोग कर सकते हैं। बस उदाहरण को मैन्युअल रूप से पास करें: 'Class.instance_method (ए)' – warvariuc

+0

@warwaruk यह वहां है, 'TyeError' रेखा के नीचे की रेखा को देखें। – kzh

+0

हाँ मैंने इसे बाद में देखा। फिर भी, आईएमओ, यह कहना सही नहीं है कि 'कक्षा एक उदाहरण विधि का उपयोग नहीं कर सकती है', क्योंकि आपने अभी इसे नीचे एक पंक्ति दी है। – warvariuc

1

कृपया गुइडो First Class everything से इस डॉक्स पढ़ जाहिर बताया कि कैसे अनबाउंड, बाउंड तरीकों पैदा होते हैं।

2
ऊपर आर्मिन Ronacher से

सटीक विवरण, अपने जवाब पर विस्तार इतना है कि मेरे जैसे शुरुआती यह अच्छी तरह से समझ:

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

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> 
सभी गुण और एक वर्ग वस्तु के तरीकों के संदर्भ में और धारण के दौरान वर्ग उदाहरण के लिए एक संदर्भ प्राप्त करता है

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

यदि कोई सीपीओ एक वर्णक है, तो पाइथन व्याख्याकर्ता सीपीओ की __get__() विधि को उस मूल्य तक पहुंचने के लिए कॉल करता है।

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

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

उदा।

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

जहां

  • self सीपीओ है (यह सूची, str, समारोह आदि का एक उदाहरण हो सकता है) और रनटाइम
  • instance द्वारा आपूर्ति की है वर्ग के उदाहरण है, जहां इस सीपीओ को परिभाषित किया गया है (उपरोक्त वस्तु 'सी') और हमें
  • owner वह वर्ग है जहां यह सीपीओ परिभाषित किया गया है (उपरोक्त वर्ग वस्तु 'सी') और हमें आपूर्ति की आवश्यकता है। हालांकि ऐसा इसलिए है क्योंकि हम इसे सीपीओ पर बुला रहे हैं। जब हम उदाहरण पर इसे कहते हैं, हम न इस की आपूर्ति करने के बाद से क्रम उदाहरण या अपने वर्ग (बहुरूपता) की आपूर्ति कर सकते
  • 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) है (जैसा कि ऊपर बताया गया है, सी के रूप में यह पता चला जा सकता है या polymorphed की जरूरत नहीं है) और यह भी कारण है कि आप एक लेखन त्रुटि आप अगर मिलता है उस आवश्यक उदाहरण तर्क पास न करें।

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

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

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

नया लिपटे सीपीओ foo में संदर्भ के अभाव does not को एक त्रुटि फेंक और इस प्रकार सत्यापित किया जा सकता के माध्यम से लागू किया जाता है:

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

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

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

संबंधित मुद्दे