2010-11-30 14 views
18

मैं उपयोगकर्ता के फ़ंक्शन को कॉल करने के लिए इंटरफ़ेस प्रदान करके अपनी क्षमताओं का विस्तार करने के लिए अपने मॉड्यूल में से किसी एक के उपयोगकर्ताओं के लिए क्षमता प्रदान करना चाहता हूं। उदाहरण के लिए, मैं उपयोगकर्ताओं को अधिसूचित होने की क्षमता देना चाहता हूं जब कक्षा का एक उदाहरण बनाया जाता है और इसका उपयोग करने से पहले उदाहरण को संशोधित करने का अवसर दिया जाता है।पायथन में एक हुक या कॉलबैक को लागू करने का पसंदीदा तरीका क्या है?

तरह से मैं यह एक मॉड्यूल स्तर के कारखाना समारोह है कि इन्स्टेन्शियशन करता घोषित करने के लिए है क्रियान्वित किया है: जब मैं mymodule में एक वर्ग का एक उदाहरण की जरूरत

# in mymodule.py 
def factory(cls, *args, **kwargs): 
    return cls(*args, **kwargs) 

फिर, मैं factory(cls, arg1, arg2) बजाय cls(arg1, arg2) करना ।

ऊपर कॉलबैक की
def myFactory(cls, *args, **kwargs): 
    instance = myFactory.chain(cls, *args, **kwargs) 
    # do something with the instance here if desired 
    return instance 

स्थापना इस तरह दिखता है:

यह विस्तार करने के लिए, एक प्रोग्रामर एक और मॉड्यूल में इस तरह एक समारोह लिखते थे

myFactory.chain, mymodule.factory = mymodule.factory, myFactory 

यह मैं करने के लिए पर्याप्त सरल लगता है, लेकिन मैं सोच रहा था कि यदि आप एक पायथन प्रोग्रामर के रूप में, किसी फ़ंक्शन को असाइनमेंट के बजाय कॉलबैक पंजीकृत करने की अपेक्षा करेंगे, या यदि अन्य विधियां आप अपेक्षा करेंगे। क्या मेरा समाधान व्यावहारिक, बेवकूफ और स्पष्ट है?

मैं इसे यथासंभव सरल रखना चाहता हूं; मुझे नहीं लगता कि ज्यादातर अनुप्रयोगों को वास्तव में एक से अधिक उपयोगकर्ता कॉलबैक को चेन करने की आवश्यकता होगी, उदाहरण के लिए (हालांकि उपरोक्त पैटर्न के साथ असीमित चेनिंग "मुफ्त में" आती है)। मुझे संदेह है कि उन्हें कॉलबैक हटाने या प्राथमिकताओं या आदेश निर्दिष्ट करने की आवश्यकता होगी। python-callbacks या PyDispatcher जैसे मॉड्यूल मुझे ओवरकिल, विशेष रूप से उत्तरार्द्ध की तरह लगते हैं, लेकिन यदि मेरे मॉड्यूल के साथ काम कर रहे प्रोग्रामर को आकर्षक लाभ हैं, तो मैं उनके लिए खुला हूं।

+1

कक्षा बढ़ाने के लिए एक सरल उपclass का उपयोग करने में क्या गलत है? यह सब भ्रम कॉल-बैक व्यवसाय क्यों? –

उत्तर

7

एक डेकोरेटर और एक वर्ग के इग्नेसियो के विचार है कि की एक सूची रखता का उपयोग करने का हारून के विचार का मेल संलग्न कॉलबैक, साथ ही सी # से उधार ली गई अवधारणा, मैं इसके साथ आया:

class delegate(object): 

    def __init__(self, func): 
     self.callbacks = [] 
     self.basefunc = func 

    def __iadd__(self, func): 
     if callable(func): 
      self.__isub__(func) 
      self.callbacks.append(func) 
     return self 

    def callback(self, func): 
     if callable(func): 
      self.__isub__(func) 
      self.callbacks.append(func) 
     return func 

    def __isub__(self, func): 
     try: 
      self.callbacks.remove(func) 
     except ValueError: 
      pass 
     return self 

    def __call__(self, *args, **kwargs): 
     result = self.basefunc(*args, **kwargs) 
     for func in self.callbacks: 
      newresult = func(result) 
      result = result if newresult is None else newresult 
     return result 

@delegate के साथ सजाए गए फ़ंक्शन को अन्य कार्यों को "संलग्न" करने की अनुमति मिलती है।

@delegate 
def intfactory(num): 
    return int(num) 

कार्य += साथ प्रतिनिधि को जोड़ा जा सकता है (और -= निकाल दिया गया)। कॉलबैक फ़ंक्शन जोड़ने के लिए आप funcname.callback से भी सजाने के लिए तैयार कर सकते हैं।

@intfactory.callback 
def notify(num): 
    print "notify:", num 

def increment(num): 
    return num+1 

intfactory += increment 
intfactory += lambda num: num * 2 

print intfactory(3) # outputs 8 

क्या यह पाइथोनिक महसूस करता है?

+0

एक प्रतिनिधि की तरह अधिक महसूस करता है, जो बिल्कुल पाइथोनिक * प्रति से * नहीं है, लेकिन मुझे इस तरह से ऐसा करने में कोई समस्या नहीं है। –

+0

हाँ, जब मैं इसके बारे में कुछ और सोच रहा था, मैंने सोचा कि यह सी # प्रतिनिधियों की तरह बहुत था। शायद मुझे वास्तव में वर्ग "प्रतिनिधि" नाम देना चाहिए। – kindall

+0

इसके बारे में और भी सोचते हुए, मुझे वास्तव में मेटाक्लास की आवश्यकता नहीं है। मैं मूल रूप से वर्ग को तत्काल किए बिना कार्यक्षमता प्राप्त करने में सक्षम होना चाहता था, लेकिन जिस तरह से मैंने इसे अभी लिखा है, यह शायद तत्कालता के साथ अधिक समझदार होगा। कुछ और काम शायद वांछित है। – kindall

6

मैं एक सजावटी का उपयोग कर सकता हूं ताकि उपयोगकर्ता बस लिख सके।

@new_factory 
def myFactory(cls, *args, **kwargs): 
    instance = myFactory.chain(cls, *args, **kwargs) 
    # do something with the instance here if desired 
    return instance 
अपने मॉड्यूल में

फिर,

import sys 

def new_factory(f): 
    mod = sys.modules[__name__] 
    f.chain = mod.factory 
    mod.factory = f 
    return f 
+0

यह एक दिलचस्प दृष्टिकोण है।मैं एक रैपर जोड़ सकता हूं जो 'चेन' कॉल करता है, ताकि उपयोगकर्ता को उस हिस्से को लिखना पड़े। यह उपयोग मामले का भी समर्थन करता है जहां उपयोगकर्ता कुछ समय पर कॉलबैक फ़ंक्शन को फ़ंक्शन परिभाषित करने के अलावा जोड़ना चाहता है। – kindall

11

थोड़ा आगे aaronsterling's idea ले रहा है:

class C(object): 
    _oncreate = [] 

    def __new__(cls): 
    return reduce(lambda x, y: y(x), cls._oncreate, super(C, cls).__new__(cls)) 

    @classmethod 
    def oncreate(cls, func): 
    cls._oncreate.append(func) 

c = C() 
print hasattr(c, 'spew') 

@C.oncreate 
def spew(obj): 
    obj.spew = 42 
    return obj 

c = C() 
print c.spew 
+0

यह कक्षा को तत्काल करने के विशिष्ट उपयोग मामले के लिए वास्तव में एक शानदार दृष्टिकोण है। – kindall

+0

सामान्य तरीकों के लिए भी वही काम किया जा सकता है। –