2011-03-07 11 views
5

मैं अजगर में एक समस्या है, जिसके लिए मैं किसी भी स्वच्छ समाधान नहीं मिल सकता है ...पायथन: metaclass + लिपटे तरीकों + विरासत = समस्याओं

जब कुछ तरीकों बुला, मैं विधि क्रियान्वयन से पहले कुछ कोड निष्पादित करने के लिए चाहते हैं और बाद में। context चर स्वचालित रूप से सेट और साफ़ करने के क्रम में (कई अन्य चीजों के बीच)। ,

class Parent(object): 

    __metaclass__ = MyType 

    def test(self): 
     #Here the context is set: 
     print self.context #prints blabla 

लेकिन जैसे ही मैं Parent उपवर्ग करना चाहते हैं के रूप में, समस्याओं दिखाई देते हैं:

class MyType(type): 
    def __new__(cls, name, bases, attrs): 
     #wraps the 'test' method to automate context management and other stuff 
     attrs['test'] = cls.other_wrapper(attrs['test']) 
     attrs['test'] = cls.context_wrapper(attrs['test']) 
     return super(MyType, cls).__new__(cls, name, bases, attrs) 

    @classmethod 
    def context_wrapper(cls, operation): 
     def _manage_context(self, *args, **kwargs): 
      #Sets the context to 'blabla' before the execution 
      self.context = 'blabla' 
      returned = operation(self, *args, **kwargs) 
      #Cleans the context after execution 
      self.context = None 
      return returned 
     return _manage_context 

    @classmethod 
    def other_wrapper(cls, operation): 
     def _wrapped(self, *args, **kwargs): 
      #DO something with self and *args and **kwargs 
      return operation(self, *args, **kwargs) 
     return _wrapped 

यह एक आकर्षण की तरह काम करता है:

इस लक्ष्य को हासिल करने के लिए, मैं निम्नलिखित metaclass घोषणा की है जब मैं super के साथ मूल विधि को कॉल करता हूं:

class Child(Parent): 
    def test(self): 
     #Here the context is set too 
     print self.context #prints blabla 
     super(Child, self).test() 
     #But now the context is unset, because Parent.test is also wrapped by _manage_context 
     #so this prints 'None', which is not what was expected 
     print self.context 

मैंने इसे नए मान पर सेट करने से पहले संदर्भ को सहेजने का विचार किया है, लेकिन यह केवल आंशिक रूप से समस्या हल करता है ...

दरअसल, (लटकाओ, यह समझाना मुश्किल है), मूल विधि कहा जाता है, रैपर क्रियान्वित कर रहे हैं, लेकिन वे *args प्राप्त करते हैं और **kwargsParent.test को संबोधित करते हुए self एक Child उदाहरण है, इसलिए self विशेषताओं अप्रासंगिक मान हो अगर मैं उन्हें *args और **kwargs (स्वचालित सत्यापन प्रयोजन के लिए उदाहरण के लिए) के साथ चुनौती देना चाहते हैं, उदाहरण के:

@classmethod 
def validation_wrapper(cls, operation): 
    def _wrapped(self, *args, **kwargs): 
     #Validate the value of a kwarg 
     #But if this is executed because we called super(Child, self).test(... 
     #`self.some_minimum` will be `Child.some_minimum`, which is irrelevant 
     #considering that we called `Parent.test` 
     if not kwarg['some_arg'] > self.some_minimum: 
      raise ValueError('Validation failed') 
     return operation(self, *args, **kwargs) 
    return _wrapped 

तो मूल रूप से, हल करने के लिए ई इस समस्या को मैं दो समाधान देखें:

  1. रैपर को रोकने निष्पादित करने के लिए जब विधि super(Child, self)

  2. एक self "सही" प्रकार की हमेशा होता है कि होने के साथ बुलाया गया था

दोनों समाधान मेरे लिए असंभव प्रतीत होते हैं ... क्या किसी को यह हल करने का विचार है? एक सुझाव ?

+0

आप इसके लिए सजावट का उपयोग क्यों नहीं कर सकते? –

+0

क्या आप पाइथन में किसी प्रकार के पहलू उन्मुख प्रोग्रामिंग समर्थन की तलाश में हैं? इसके लिए कुछ विचारों के लिए http://stackoverflow.com/questions/286958/any-aop-support-library-for-python देखें। यह वही नहीं है जो आप पूछ रहे हैं, बल्कि यह आपकी मूल जरूरतों को हल करने की कोशिश कर रहा है। – Makis

+0

@ Space_C0wb0y: क्योंकि मैं 5 सजावटकर्ताओं के साथ पुनर्व्यवस्थित किए बिना, सबक्लास में 'टेस्ट' को फिर से चलाने में सक्षम होना चाहता हूं! और वैसे भी जो मेरी समस्या का समाधान नहीं करेंगे: जिस तरह से मैं मेटाक्लास से विधियों को लपेटता हूं वह सजावट करने वालों के बराबर है। – sebpiq

उत्तर

0

असल में मैं जब विधि super(Child, self) साथ बुलाया गया था रैपर को रोकने के लिए निष्पादित करने के लिए एक तरह से पता चला है पास-पास !!! यह काफी हैकिश है, लेकिन यह काम करता है ...

1

ठीक है, क्या आप सिर्फ यह जांच नहीं सकते कि संदर्भ _manage_context में पहले से सेट है या नहीं? इस तरह:

def _manage_context(self, *args, **kwargs): 
    #Sets the context to 'blabla' before the execution 
    if self.context is None: 
     self.context = 'blabla' 
     returned = operation(self, *args, **kwargs) 
     #Cleans the context after execution 
     self.context = None 
     return returned 
    else: 
     return operation(self, *args, **kwargs) 

इसके अलावा, यह शायद एक कोशिश पकड़ ब्लॉक में लिपटे किया जाना चाहिए, अपवाद के मामले में संदर्भ से रीसेट सुनिश्चित करने के लिए। जब बुला

class MyType(type): 
    def __new__(cls, name, bases, attrs): 
     #wraps the 'test' method to automate context management and other stuff 
     new_class = super(MyType, cls).__new__(cls, name, bases, attrs) 
     new_class.test = cls.other_wrapper(new_class.test, new_class) 

    @classmethod 
    def other_wrapper(cls, operation, new_class): 
     def _wrapped(self, *args, **kwargs): 
      #DO something with self and *args and **kwargs ... 
      #ONLY if self is of type *new_class* !!! 
      if type(self) == new_class: 
       pass #do things 
      return operation(self, *args, **kwargs) 
     return _wrapped 

इस तरह,:

super(Child, self).a_wrapped_method 

रैपिंग कोड है

+0

यह एक विचार है ... हालांकि अगर किसी कारण से संदर्भ विधि के बाहर से सेट किया गया है तो यह तोड़ने जा रहा है। साथ ही, यह जांचने के लिए कि क्या पहले से ही निष्पादन से पहले सेट सेट किया गया है, अन्य सभी रैपरों की आवश्यकता है। – sebpiq

0

ठीक है, पहले, आपका "समाधान" वास्तव में बदसूरत है, लेकिन मुझे लगता है कि आप उसे जानते हैं। :-) तो चलिए अपने सवालों के जवाब देने का प्रयास करें।

पहला एक निहित "प्रश्न" है: आप पाइथन के संदर्भ प्रबंधकों का उपयोग क्यों नहीं करते? वे आपको व्यावहारिक रूप से मुफ्त में बहुत अच्छे सिंटैक्स और त्रुटि प्रबंधन देते हैं। Contextlib मॉड्यूल देखें, यह आपको बहुत मदद कर सकता है। विशेष रूप से section about reentrancy देखें।

फिर आप देखेंगे कि संदर्भ प्रबंधक को ढेर करने की कोशिश करते समय लोगों को आमतौर पर समस्याएं होती हैं। यह आश्चर्य की बात नहीं है, क्योंकि रिकर्सन का सही ढंग से समर्थन करने के लिए आपको मूल्यों का ढेर चाहिए, एक मूल्य नहीं। [आप उदाहरण redirect_stdout के लिए कुछ रैत्रांत सेमी के लिए स्रोत देख सकते हैं, यह देखने के लिए कि यह कैसे संभाला है।] तो अपने context_wrapper चाहिए या तो:

  • (क्लीनर), self.context रों की एक सूची रखना इसे करने के लिए संलग्न जब में प्रवेश संदर्भ, और बाहर निकलने पर इससे पॉप। इस तरह आप हमेशा अपने संदर्भ प्राप्त करते हैं।

  • (आप क्या चाहते हैं की तरह अधिक) एक एकल self.context रखने के लिए, लेकिन यह भी एक वैश्विक मूल्य DEPTH, में प्रवेश करने पर एक की वृद्धि हुई, बाहर निकलने, और self.context को कोई नहीं पर रीसेट किया जा रहा पर एक की कमी हुई जब गहन 0.

    है

आपके दूसरे प्रश्न के लिए, मुझे कहना होगा कि मैं आपको काफी समझ नहीं पा रहा हूं। selfसही प्रकार के है। यदि ए बी का उप-वर्ग है, और स्वयं ए का उदाहरण है, तो यह बी का भी उदाहरण है। यदि self.some_minimum "गलत" है, तो क्या आप स्वयं को ए या बी का उदाहरण मानते हैं, जिसका अर्थ है कि कुछ_मिनेम वास्तव में नहीं है उदाहरण का स्वयं का गुण, लेकिन ए या बी की कक्षा विशेषता सही है? वे ए और बी पर स्वतंत्र रूप से भिन्न हो सकते हैं, क्योंकि ए और बी अलग-अलग ऑब्जेक्ट्स (उनके मेटाक्लास) हैं।

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