2012-12-30 12 views
7

मेरे पास एक ऑब्जेक्ट पदानुक्रम है जिसमें लगभग सभी विधियां कक्षा विधियां हैं।पायथन - क्या मैं क्लास उदाहरण से वर्ग विधियों को प्रोग्रामेटिक रूप से सजाने सकता हूं?

class ParentObject(object): 
    def __init__(self): 
     pass 

    @classmethod 
    def smile_warmly(cls, the_method): 
     def wrapper(kls, *args, **kwargs): 
      print "-smile_warmly - "+kls.__name__ 
      the_method(*args, **kwargs) 
     return wrapper 

    @classmethod 
    def greetings(cls): 
     print "greetings" 

class SonObject(ParentObject): 
    @classmethod 
    def hello_son(cls): 
     print "hello son" 

    @classmethod 
    def goodbye(cls): 
     print "goodbye son" 

class DaughterObject(ParentObject): 
    @classmethod 
    def hello_daughter(cls): 
     print "hello daughter" 

    @classmethod 
    def goodbye(cls): 
     print "goodbye daughter" 

if __name__ == '__main__': 
    son = SonObject() 
    son.greetings() 
    son.hello_son() 
    son.goodbye() 
    daughter = DaughterObject() 
    daughter.greetings() 
    daughter.hello_daughter() 
    daughter.goodbye() 

कोड दिया आउटपुट के रूप में निम्नलिखित:

greetings 
hello son 
goodbye son 
greetings 
hello daughter 
goodbye daughter 

मैं चाहूँगा उत्पादन के लिए कोड का पालन:

-smile_warmly - SonObject 
greetings 
-smile_warmly - SonObject 
hello son 
-smile_warmly - SonObject 
goodbye son 
-smile_warmly - DaughterObject 
greetings 
-smile_warmly - DaughterObject 
hello daughter 
-smile_warmly - DaughterObject 
goodbye daughter 

लेकिन मुझे नहीं पता यह निम्नलिखित की तरह दिखता है प्रत्येक विधि से पहले लाइन @smile_warmly जोड़ना चाहते हैं (और जब मैं ऊपर करने के लिए कि कोड में प्रयास करते हैं, तो मुझे त्रुटि संदेश TypeError: 'classmethod' object is not callable मिल)। इसके बजाय, मैं __init__() विधि में प्रोग्रामेटिक रूप से प्रत्येक विधि की सजावट करना चाहता हूं।

क्या यह संभव है प्रोग्राम के रूप में अजगर में तरीकों को सजाने के लिए?

EDIT: कुछ ऐसा लगता है जो काम करने लगता है - नीचे मेरा उत्तर देखें। ब्रेनबर्न के लिए धन्यवाद।

+1

आप परिचित metaclasses की अवधारणा है? –

+0

लगता है जैसे आप इसे देखना चाहते हैं: http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python शायद अगर आप इस लड़के को सजावटी का उपयोग करने पर भी सेट कर रहे हैं: http://stackoverflow.com/questions/739654/understanding-python-decorators –

+0

मैंने उनके बारे में सुना है, लेकिन यह इसके बारे में है। तो नहीं। – jononomo

उत्तर

14

सभी एक डेकोरेटर करता है एक नया कार्य वापसी है। यह:

@deco 
def foo(): 
    # blah 

इस रूप में ही है:

def foo(): 
    # blah 
foo = deco(foo) 

आप एक ही बात आप जब चाहें, @ वाक्य रचना के बिना, बस कार्यों जो कुछ के साथ आप की तरह की जगह कर सकते हैं। __init__ में या फिर जहाँ भी तो, आप सभी तरीकों के माध्यम से और हर एक के लिए पाश smilewarmly(meth) से बदलने सकता है।

हालांकि, बजाय __init__ में यह कर के, यह यह करने के लिए जब वर्ग बनाई गई है और अधिक समझ बनाने जाएगा। इस उदाहरण मैं अपने smileDeco डेकोरेटर के अंदर classmethod सजावट डाल में

def smileDeco(func): 
    def wrapped(*args, **kw): 
     print ":-)" 
     func(*args, **kw) 
    return classmethod(wrapped) 

def makeSmiley(cls): 
    for attr, val in cls.__dict__.iteritems(): 
     if callable(val) and not attr.startswith("__"): 
      setattr(cls, attr, smileDeco(val)) 
    return cls 

@makeSmiley 
class Foo(object): 
    def sayStuff(self): 
     print "Blah blah" 

>>> Foo().sayStuff() 
:-) 
Blah blah 

: आप इस एक वर्ग डेकोरेटर के साथ और अधिक बस एक metaclass के साथ कर सकता है, या। आप इसे makeSmiley में भी डाल सकते हैं ताकि makeSmileysmileDeco(classmethod(val)) लौटा। (किस तरह आप क्या करना चाहते हैं कि यह कैसे जुड़े हुए मुस्कान-डेकोरेटर बातें classmethods किया जा रहा है पर निर्भर करता है।) इसका मतलब है आप वर्ग के अंदर @classmethod उपयोग करने के लिए नहीं है।

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

ध्यान दें कि यदि आप वाकई कक्षा के अंदर @classmethod मैन्युअल रूप से उपयोग करना चाहते हैं, तो आपको थोड़ा और सावधान रहना होगा, क्योंकि क्लास __dict__ के माध्यम से एक्सेस किए गए क्लासमेड्स कॉल करने योग्य नहीं हैं। तो आपको यह जांचना होगा कि ऑब्जेक्ट क्लासमेड ऑब्जेक्ट है या नहीं, यह जांचने के बजाय कि यह कॉल करने योग्य है या नहीं।

+0

यह बहुत उपयोगी लगता है। मैं इसे अभी काम करने की कोशिश कर रहा हूं और अगर मैं इसे खींच सकता हूं तो यहां एक अपडेट प्रदान करेगा। – jononomo

+0

यदि मेरे पास दर्जन वर्ग हैं जो मेरे माता-पिता वर्ग को उप-वर्ग करते हैं, तो मुझे प्रत्येक वर्ग को सजाने के लिए याद रखना होगा। मैं केवल तर्क प्राप्त करना चाहता हूं जो विधियों को समायोजित करता है '__init __()' विधि में स्वचालित रूप से होता है। मेरे बच्चे वर्ग __init __() को ओवरराइड नहीं करेंगे, इसलिए माता-पिता वर्ग में परिभाषित '__init __()' को हमेशा बाल कक्षा के निर्माण के समय बुलाया जाएगा। अगर मैं वहां समान तर्क डाल सकता हूं, तो मेरे पास इच्छित कार्यक्षमता होगी। तो मैं यही काम कर रहा हूं। – jononomo

+0

@ जोनक्रॉवेल: आप ऐसा कर सकते हैं, लेकिन प्रत्येक * इंस्टेंस * के लिए '__init__' कहा जाता है, हर वर्ग नहीं, इसलिए यह आपके जैसा दिखता नहीं है। यदि आप व्यवहार को पूरी तरह से स्वचालित करना चाहते हैं तो आपको मेटाक्लास का उपयोग करना होगा। आप मेटाक्लास पर पढ़ सकते हैं [यहां] (http://docs.python.org/2/reference/datamodel.html#customizing-class-creation) और [यहां] (http://stackoverflow.com/questions/100003/क्या-है-एक metaclass में अजगर)। – BrenBarn

0

यह समाधान का उत्पादन उत्पादन मैं इच्छा:

class ParentObject(object): 
    def __init__(self): 
     self._adjust_methods(self.__class__) 

    def _adjust_methods(self, cls): 
     for attr, val in cls.__dict__.iteritems(): 
      if callable(val) and not attr.startswith("_"): 
       setattr(cls, attr, self._smile_warmly(val)) 
     bases = cls.__bases__ 
     for base in bases: 
      self._adjust_methods(base) 

    def _smile_warmly(self, the_method): 
     def _wrapped(self, *args, **kwargs): 
      print "-smile_warmly - " +self.__name__ 
      the_method(self, *args, **kwargs) 
     cmethod_wrapped = classmethod(_wrapped) 
     # cmethod_wrapped.adjusted = True 
     return cmethod_wrapped 

    def greetings(self): 
     print "greetings" 

class SonObject(ParentObject): 
    def hello_son(self): 
     print "hello son" 

    def goodbye(self): 
     print "goodbye son" 

class DaughterObject(ParentObject): 
    def hello_daughter(self): 
     print "hello daughter" 

    def goodbye(self): 
     print "goodbye daughter" 

if __name__ == '__main__': 
    son = SonObject() 
    son.greetings() 
    son.hello_son() 
    son.goodbye() 
    daughter = DaughterObject() 
    daughter.greetings() 
    daughter.hello_daughter() 
    daughter.goodbye() 

उत्पादन होता है:

-smile_warmly - SonObject 
greetings 
-smile_warmly - SonObject 
hello son 
-smile_warmly - SonObject 
goodbye son 
-smile_warmly - DaughterObject 
greetings 
-smile_warmly - DaughterObject 
hello daughter 
-smile_warmly - DaughterObject 
goodbye daughter 
+0

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

+0

हम्म - मुझे लगता है कि मैं खराब हो सकता था। यहां दिया गया मेरा खिलौना उदाहरण मुझे इच्छित आउटपुट देता है, लेकिन मेरा वास्तविक कोड, जो अधिक जटिल है, मुझे परेशान कर रहा है 'टाइप एरर:' कोई नहीं टाइप 'ऑब्जेक्ट कॉल करने योग्य नहीं है' संदेश और क्रैशिंग। – jononomo

+0

आप उस मुद्दे के साथ एक नया प्रश्न पोस्ट करना चाहेंगे। – BrenBarn

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