2012-04-29 18 views
13

मुझे आशा है कि किसी को इस अजगर का एक अच्छा गहरी समझ :)एक उदाहरण पर अधिभावी विशेष तरीकों

निम्नलिखित कोड पर विचार करें है कि जवाब कर सकते हैं:

>>> class A(object): 
...  pass 
... 
>>> def __repr__(self): 
...  return "A" 
... 
>>> from types import MethodType 
>>> a = A() 
>>> a 
<__main__.A object at 0x00AC6990> 
>>> repr(a) 
'<__main__.A object at 0x00AC6990>' 
>>> setattr(a, "__repr__", MethodType(__repr__, a, a.__class__)) 
>>> a 
<__main__.A object at 0x00AC6990> 
>>> repr(a) 
'<__main__.A object at 0x00AC6990>' 
>>> 

सूचना कैसे रेपर (क) उपज नहीं है "ए" का अपेक्षित परिणाम? मुझे पता है क्यों यह मामला है चाहते हैं और अगर वहाँ इस लक्ष्य को हासिल करने के लिए एक तरह से ...

मैं इसके विपरीत है, निम्न उदाहरण हालांकि काम करता है (हो सकता है कि क्योंकि हम एक विशेष विधि ओवरराइड करने के लिए कोशिश नहीं कर रहे?):

>>> class A(object): 
...  def foo(self): 
...    return "foo" 
... 
>>> def bar(self): 
...  return "bar" 
... 
>>> from types import MethodType 
>>> a = A() 
>>> a.foo() 
'foo' 
>>> setattr(a, "foo", MethodType(bar, a, a.__class__)) 
>>> a.foo() 
'bar' 
>>> 
+0

यदि आप कहते हैं कि आप आखिरकार क्या हासिल करने की कोशिश कर रहे हैं तो आपको शायद अधिक प्रबुद्ध प्रतिक्रिया मिलेगी। – mattmanser

+1

नई शैली के वर्गों के लिए, उदाहरण के बजाए कक्षा पर विशेष तरीकों को देखा जाता है। अधिक जानकारी के लिए और कुछ कामकाज के लिए नीचे मेरा उत्तर देखें। –

+0

मैं वर्णन नहीं कर सका कि मैं विस्तार से क्या करना चाहता हूं। माफ़ कीजिये। लेकिन संक्षेप में, मैं एक प्रोटोटाइप ओओ मॉडल मॉडल करने की कोशिश कर रहा हूं जहां मैं ऑपरेटर कर सकता हूं जैसे: वर्ल्ड = ऑब्जेक्ट() क्लोन()। मिक्सीन (वर्ल्ड); जहां दुनिया उन विधियों के संग्रह के साथ एक वर्ग है जो ऑब्जेक्ट.क्लोन() उदाहरण में ओवरराइड/प्रतिस्थापित करता है। –

उत्तर

15

पायथन विशेष तरीकों को नहीं बुलाता है, उदाहरण के साथ __ से घिरे नाम वाले नाम, लेकिन केवल प्रदर्शन में सुधार के लिए कक्षा में ही। इसलिए __repr__() को सीधे एक उदाहरण पर ओवरराइड करने और इसे काम करने का कोई तरीका नहीं है। इसके बजाय, आप तो कुछ ऐसा करने की जरूरत है:

class A(object): 
    def __repr__(self): 
     return self._repr() 
    def _repr(self): 
     return object.__repr__(self) 

अब आप _repr() प्रतिस्थापन एक उदाहरण पर __repr__() ओवरराइड कर सकते हैं।

+0

"पायथन विशेष तरीकों को नहीं बुलाता है, उदाहरण के लिए __ से घिरे नाम वाले लोग, लेकिन प्रदर्शन में सुधार के लिए जाहिर तौर पर कक्षा में" <- क्या आपके पास इसका संदर्भ है? –

+1

देखें [यह] (http://docs.python.org/reference/datamodel.html#special-method-lookup-for- new-style-classes)। पुराने शैली के वर्गों पर नियम थोड़ा आराम से हैं, लेकिन आपको नई शैली के वर्गों का उपयोग करना चाहिए, और पायथन 3 में वे वैसे भी एकमात्र प्रकार हैं। – kindall

+5

यह मुख्य रूप से प्रदर्शन में सुधार करने के लिए नहीं है। जैसा कि दस्तावेज़ कहते हैं, "इस व्यवहार के पीछे तर्क कई विशेष तरीकों से है, जैसे' __hash __() 'और' __repr __() 'जो कि ऑब्जेक्ट्स सहित सभी ऑब्जेक्ट्स द्वारा कार्यान्वित किए जाते हैं। यदि इन विधियों का अंतर्निहित लुकअप इस्तेमाल होता है परंपरागत लुकअप प्रक्रिया, जब वे ऑब्जेक्ट पर ऑब्जेक्ट करते हैं तो वे विफल हो जाएंगे ... "(हालांकि, '__getattribute__' _also_ के लिए तर्क छोड़ने योग्य _is_ प्रदर्शन।) – abarnert

3

इस का कारण यह विशेष तरीके (__x__()) वर्ग, नहीं उदाहरण के लिए निर्धारित किए जाते हैं।

यह समझ में आता है जब आप __new__() के बारे में सोचते हैं - यह एक उदाहरण पर कॉल करना असंभव होगा क्योंकि उदाहरण कहने पर यह अस्तित्व में नहीं है।

>>> A.__repr__ = __repr__ 
>>> a 
A 

या एक व्यक्ति के उदाहरण पर, in kindall's answer के रूप में:

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

3

नई शैली कक्षाओं के लिए, पायथन एक विशेष विधि लुकअप का उपयोग करता है जो उदाहरणों को छोड़ देता है। यहाँ the source से एक अंश:

1164 /* Internal routines to do a method lookup in the type 
    1165 without looking in the instance dictionary 
    1166 (so we can't use PyObject_GetAttr) but still binding 
    1167 it to the instance. The arguments are the object, 
    1168 the method name as a C string, and the address of a 
    1169 static variable used to cache the interned Python string. 
    1170 
    1171 Two variants: 
    1172 
    1173 - lookup_maybe() returns NULL without raising an exception 
    1174  when the _PyType_Lookup() call fails; 
    1175 
    1176 - lookup_method() always raises an exception upon errors. 
    1177 
    1178 - _PyObject_LookupSpecial() exported for the benefit of other places. 
    1179 */ 

आप या तो एक पुरानी शैली वर्ग के लिए परिवर्तन (वस्तु से विरासत नहीं है) कर सकते हैं या आप (तरीकों कि आगे के लिए वापस lookups वर्ग के लिए डिस्पैचर विधियां जोड़ सकते हैं उदाहरण)। उदाहरण डिस्पैचर तरीकों में से एक उदाहरण के लिए, पर http://code.activestate.com/recipes/578091

+0

AFAIK अगर मैं पुराने स्टाइल कक्षाओं का उपयोग करने के लिए बदलना चाहता था, तो यह पायथन 3 के लिए काम नहीं करेगा? –

7

के रूप में नुस्खा देखने Special Method Lookup में विस्तार से बताया:

कस्टम वर्गों के लिए, विशेष तरीकों की अंतर्निहित आमंत्रण केवल सही ढंग से काम करने के लिए यदि एक वस्तु के प्रकार पर परिभाषित की गारंटी है, वस्तु के उदाहरण शब्दकोश में नहीं है ... इसके अलावा करने के लिए किसी भी घटना को दरकिनार शुद्धता के हित में गुण, अंतर्निहित विशेष विधि देखने आम तौर पर भी __getattribute__() विधि वस्तु की metaclass की भी नजरअंदाज

(यदि मैंने उसमें रूचि रखी है, तो मैंने जो हिस्सा छीन लिया है, उसके पीछे तर्क बताता है।)

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

जैसा कि आप अपने परीक्षण परिणामों से अनुमान लगा सकते हैं, सीपीथॉन कार्यान्वयन में, __repr__ इस प्रकार के कार्यों में से एक है।


चीजें ज्यादातर क्लासिक वर्गों की मौजूदगी के कारण, 2.x में थोड़ा अलग हैं, लेकिन जब तक आप केवल बना रहे हैं नई शैली कक्षाओं आप एक ही रूप में उनमें से सोच सकते हैं।


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

स्वच्छ समाधान कक्षा पर एक विशेष विधि को लागू करना है जो केवल उदाहरण पर एक नियमित विधि कहता है। फिर आप बंदर पैच कर सकते हैं कि प्रत्येक उदाहरण पर नियमित विधि। उदाहरण के लिए:

class C(object): 
    def __repr__(self): 
     return getattr(self, '_repr')() 
    def _repr(self): 
     return 'Boring: {}'.format(object.__repr__(self)) 

c = C() 
def c_repr(self): 
    return "It's-a me, c_repr: {}".format(object.__repr__(self)) 
c._repr = c_repr.__get__(c) 

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

+0

मुझे लगता है कि 2.x पर काम करना है, आपको '__get __ (सी, टाइप (सी)) 'कॉल करने की आवश्यकता हो सकती है, लेकिन मुझे निश्चित रूप से याद नहीं है, और इस मशीन पर काम करने वाला 2.x दुभाषिया नहीं है ... हो सकता है कि इसके बजाय 'type.MethodType' का उपयोग करना बेहतर होगा? – abarnert

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