2012-01-04 20 views
7

मेरे पास मुख्य परियोजना में एक कक्षा है जिसे मैं बदलना नहीं चाहता हूं।जब यह बंदर-पैच होता है तो मूल विधि को कैसे कॉल करें?

class A(): 
    def __init__(self, firstname, lastname): 
     self.firstname = firstname 
     self.lastname = lastname 

    def name(self): 
     # this method could be much more complex 
     return self.lastname.upper() 

मैं एक प्लगइन मेचांसिम बनाने की कोशिश कर रहा हूं।

if __name__ == '__main__': 
    ''' The main project has an extension point that allows me to do''' 
    # for each class extension such as AExtended: 
    A.name = AExtended.name 
    ''' After the extensions are loaded, some behaviours may be changed''' 
    a = A("John", "Doe") 
    print(a.name()) 

एक प्लगइन इस तरह लिखा जा सकता है:

class AExtended(A): 
    ''' This is an extension I provide through a plugin mechanism 
    ''' 
    def name(self): 
     return self.firstname + ' ' + self.lastname.upper() 

यह सब बहुत अच्छी तरह से काम करता है अब तक तो अच्छा, मैं इस तरह एक विस्तार बिंदु है। अब मुझे "जॉन डीओई" मिलता है।

मेरी समस्या यह है कि मूल name() विधि काफी जटिल हो सकती है। दूसरे शब्दों में, मैं AExtended में self.lastname.upper() पर कॉल करने का जोखिम नहीं उठा सकता। मैं "सुपर" विधि को कॉल करना चाहता हूं, जो अब अस्तित्व में नहीं है, क्योंकि इसे ओवरराइट किया गया है।

मैं कैसे कुछ इस तरह प्राप्त करने के लिए अपना कोड बदल सकते हैं क्रम में,:

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + parent.name() 

आपकी मदद के लिए धन्यवाद!

संपादित करें: मैं जो करने की कोशिश करता हूं उसके कुछ स्पष्टीकरण।

  • मैं चाहता हूं कि प्लगइन A के व्यवहार को पैच करे। मैं की मौजूदा उपभोक्ताओं को बदलने के लिए खर्च नहीं उठा सकते एक
  • A जैसे कई कक्षाओं बदला जा सकता है कि मैं पूर्ण नियंत्रण और जिम्मेदारी
  • के लिए प्लग-इन करना चाहते हैं, यह AExtendedA से विरासत की जरूरत नहीं है सच है, लेकिन यह self.firstname तक पहुंचने का एक आसान तरीका था। अगर यह मदद कर सकता है तो मुझे एक अलग डिजाइन पैटर्न के बाद कोई समस्या नहीं है।

मैं एक समाधान है, लेकिन यह सामान्य

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + self.parentname() 
#in main 
A.parentname = A.name 
A.name = AExtended.name 
+0

हाँ, मैंने पढ़ा है कि कैसे [फोन माता पिता वर्ग विधि] के लिए (http://stackoverflow.com/questions/805066/how-to-call-a-parent-classs-method-from- बाल-वर्ग-इन-पायथन) लेकिन 'AExtended.name' में, 'self' ओवरराइडिंग – rds

+0

की वजह से' ए' है 'मैं [निजी विधि ओवरराइडिंग] के साथ अधिक लकी हो सकता हूं (http://stackoverflow.com/ प्रश्न/2484215/कैसे-करें-i-override-a-parent-classs-functions-in-python) लेकिन मैं ए विधि नाम नहीं बदल सकता। – rds

+0

अपनी स्पष्टीकरण को देखते हुए, मैं सुझाव दूंगा कि आप प्रश्न शीर्षक बदल दें। एक और सटीक शीर्षक हो सकता है "जब यह बंदर-पैच होता है तो मूल विधि को कैसे कॉल करें?" – Weeble

उत्तर

3

यहां पर एक संकेत दिया गया है कि मैं क्या संकेत दे रहा था। मुझे चिल्लाने के लिए स्वतंत्र महसूस करें और मुझे मेरे उत्तरों को मर्ज करें, या एक या जो कुछ भी कम करें, एक नया उत्तर के रूप में एक विकल्प प्रदान करना आसान है। मैं कोड को खराब तरीके से समझाए जाने की बजाय बात करने दूंगा।:)

## Some shared class that is used all over the place and needs to be patched. 

class A(object): 
    def __init__(self): 
     self.firstname = 'Bob' 

    # Print my first name. 
    def name(self): 
     return self.firstname 

    # Use this to allow patching arbitrary methods... 
    @classmethod 
    def patch(cls, func_name): 
     def patch_by_name(new_func): 
      old_func = getattr(cls, func_name) 
      def patched_func(self): 
       return new_func(self, old_func) 
      setattr(cls, func_name, patched_func) 
     return patch_by_name 

## Some other area of the code where you want to throw in a patch 

class PatchedA(A): # doesn't need to subclass, but comes in handy sometimes 
    @A.patch('name') 
    def name(self, orig_func): 
     return 'I am ' + orig_func(self) + 'McWizwaz' 

print 'Who are you, A class?' 
print A().name() # prints 'I am Bob McWizwaz' 
+0

(और बेशक, आप इसे क्लासमेड और सिर्फ एक स्टैंडअलोन फ़ंक्शन नहीं बनाते हैं और इसे '@ पैच (ए,' नाम ') के रूप में कहते हैं, इस प्रकार इसे किसी भी वर्ग के लिए कहीं भी प्रयोग योग्य बनाते हैं।) – Spencer

+0

'ए' के ​​बाहर '@ पैच' सजावट डालना आसान नहीं है और इसे पैचिंग 'डीफ़ नाम' _outside_ किसी उप-वर्ग में भी लागू करना है? जब भी आप मूल 'ए' विधि को प्रतिस्थापित करते हैं, वैसे भी उप-वर्ग 'पैचड ए' का उद्देश्य क्या है? –

0

एक विकल्प है कि हमेशा अजगर की तरह एक भाषा की सर्वश्रेष्ठ this non-standard package से @override डेकोरेटर का उपयोग करना है हो सकता है नहीं हो सकता है बहुत ही सुंदर और मुश्किल नहीं है। लेकिन यह केवल एक व्यवहार्य विकल्प है यदि आपके दो कार्य विभिन्न प्रकारों या विभिन्न तर्कों पर काम करते हैं। इसके अलावा, आपके फ़ंक्शन का नाम बदलने के अलावा, आप इतना कुछ नहीं कर सकते हैं।

+0

दिलचस्प पैकेज, लेकिन मुझे नहीं लगता कि यह मेरी मदद कैसे कर सकता है: -/ – rds

1
class ABase(object): 
    def name(self): 
     pass 

class A(object): 
    pass 

class AExtension(ABase): 
    def name(self): 
     return ABase.name(self) 

A.name = AExtension.name 
+0

मुझे यकीन नहीं है कि मैं समझता हूं कि किस वर्ग का उपयोग किया जाना चाहिए। मुझे 'ABase' से' A' ('A.name = ABase.name' आदि) से सभी विधियों की प्रतिलिपि बनाने की आवश्यकता है और फिर एक्सटेंशन लोड करें (' A.name = AExtension.name')? – rds

+0

'ए' में पहले से ही 'ABase' के सभी तरीके हैं क्योंकि' ए ''ABase' बढ़ाता है। लेकिन कुल मिलाकर अन्य वर्गों के तरीकों को पुन: असाइन करने का विचार संदिग्ध लगता है। आपको कुछ विषयों को शायद इस विषय में न्यूनतम प्लगइन आर्किटेक्चर को सही तरीके से कैसे प्राप्त किया जा सकता है: http://stackoverflow.com/questions/932069/building-a-minimal-plugin-architecture-in-python – Ski

+0

@ सिकर्मेंटस, इसे सजाने के लिए असामान्य नहीं है वह काम करता है जैसे वह करना चाहता है। भाषा के नए संस्करण भी मेरे उदाहरण को लिखना आसान बनाने के लिए वाक्य रचनात्मक चीनी का समर्थन करते हैं। – Spencer

12

यही वह है जिसे हम 'सजावट' पैटर्न कहते हैं। नाम के मूल पुन: असाइनमेंट को प्रतिस्थापित करने के लिए इसे एक फ़ंक्शन कॉल करें, जो मूल लेता है। फिर यह एक नया समारोह देता है।

def name_decorator(method): 
    def decorate_name(self=None): 
     return stuff + method(self) 
    return decorate_name 
A.name = name_decorator(A.name) 

बाद में, बुला A.nameself वर्तमान उदाहरण के रूप में और method साथ decorate_name फोन जो पुनर्निर्धारण के समय में समारोह के लिए कहते हैं यह करने के लिए उपलब्ध हो जाएगा होगा।

+0

+1 क्योंकि पाइथन सजावट ठीक वही कर रहे हैं जो मैं चाहता हूं। मैं यह जवाब स्वीकार करूंगा जब मैं जांच सकता हूं कि इसे सामान्यीकृत किया जा सकता है। – rds

+0

मैं पायथन 3 का उपयोग करता हूं। कौन सी वाक्यविन्यास चीनी मदद कर सकती है? – rds

+0

देखें: http://docs.python.org/reference/compound%5Fstmts.html#function - स्पष्ट ओवरराइड करने के बजाय, आप किसी भी फ़ंक्शन डिफ को सजाने के पहले ठीक से '@ my_decorator' रख सकते हैं। – Spencer