2010-10-14 13 views
25

मैं एक सजावट बनाना चाहता हूं जिसे पैरामीटर के साथ या उसके बिना इस्तेमाल किया जा सकता है: ऐसा कुछ:वैकल्पिक पैरामीटर के साथ एक सजावट कैसे बनाया जाए?

class d(object): 
    def __init__(self,msg='my default message'): 
     self.msg = msg 
    def __call__(self,fn): 
     def newfn(): 
      print self.msg 
      return fn() 
     return newfn 

@d('This is working') 
def hello(): 
    print 'hello world !' 

@d 
def too_bad(): 
    print 'does not work' 

मेरे कोड में, केवल पैरामीटर के साथ सजावटी का उपयोग काम कर रहा है: दोनों को कैसे आगे बढ़ना है काम कर रहा है (पैरामीटर के साथ और बिना)?

उत्तर

30

मैं एक उदाहरण पाया, तो आप उपयोग कर सकते हैं @trace या @trace('msg1','msg2'): अच्छा!

def trace(*args): 
    def _trace(func): 
     def wrapper(*args, **kwargs): 
      print enter_string 
      func(*args, **kwargs) 
      print exit_string 
     return wrapper 
    if len(args) == 1 and callable(args[0]): 
     # No arguments, this is the decorator 
     # Set default values for the arguments 
     enter_string = 'entering' 
     exit_string = 'exiting' 
     return _trace(args[0]) 
    else: 
     # This is just returning the decorator 
     enter_string, exit_string = args 
     return _trace 
+0

मुझे लगता है कि यह आपके मामले के लिए काम करता है और इसी तरह के मामलों के लिए होगा। लेकिन क्या होगा अगर सजावटी का तर्क वास्तव में एक कॉल करने योग्य है? सजाए गए कार्य और तर्क के बीच आप कैसे अंतर करेंगे? – ksrini

+1

@ksrini: इग्नासिओ ने अपने [उत्तर] (http://stackoverflow.com/a/3931654/355230) साल पहले बताया था। – martineau

+2

आप कीवर्ड तर्कों (जैसे '@trace (default = ...) 'का उपयोग कर इसके आसपास भी जा सकते हैं। – jtbandes

4

आपको यह पता लगाना होगा कि सजावटकर्ता का तर्क एक कार्य है, और उस मामले में एक साधारण सजावट का उपयोग करें। और फिर आपको उम्मीद करनी होगी कि आपको कभी भी पैरामीट्रिज्ड सजावट करने वाले को केवल एक समारोह को पारित करने की आवश्यकता नहीं है।

16

आप अपने डेकोरेटर के लिए मानकों को ले जाना चाहते हैं, तो आप की जरूरत हमेशा एक समारोह के रूप में यह कहते हैं: - दूसरे शब्दों में

@d() 
def func(): 
    pass 

अन्यथा, आप मानकों में अंतर का पता लगाने के प्रयास करने की आवश्यकता , आपको जादूगर का अनुमान लगाने की ज़रूरत है कि कॉलर का क्या अर्थ है। अनुमान लगाने की आवश्यकता वाले एपीआई को न बनाएं; लगातार कहें कि आपका क्या मतलब है।

दूसरे शब्दों में, एक समारोह या तो सजावटी या सजावटी कारखाना होना चाहिए; यह दोनों नहीं होना चाहिए।

ध्यान दें कि यदि आप जो करना चाहते हैं वह एक मूल्य स्टोर करना है, तो आपको कक्षा लिखने की आवश्यकता नहीं है।

def d(msg='my default message'): 
    def decorator(func): 
     def newfn(): 
      print msg 
      return func() 
     return newfn 
    return decorator 

@d('This is working') 
def hello(): 
    print 'hello world !' 

@d() 
def hello2(): 
    print 'also hello world' 
+1

यह शायद एक अच्छी सलाह है, लेकिन यह सही नहीं है कि एक समारोह दोनों नहीं कर सकता, क्योंकि इग्नासिओ वाज़्यूज़-एब्राम बताते हैं। उत्तर में यह समझाने के लिए शायद बेहतर है। –

+3

@ मुहम्मद: मैंने यह नहीं कहा कि यह नहीं कर सकता, मैंने कहा कि यह नहीं होना चाहिए। –

+1

मैं समझता हूं। लेकिन अगर इस बिंदु को थोड़ा बेहतर समझाया गया है तो उत्तर का मूल्य अधिक होगा। बस केह रहा हू। –

2

यह काम करेगा।

def d(arg): 
    if callable(arg): 
     def newfn(): 
      print 'my default message' 
      return arg() 
     return newfn 
    else: 
     def d2(fn): 
      def newfn(): 
       print arg 
       return fn() 
      return newfn 
     return d2 

@d('This is working') 
def hello(): 
    print 'hello world !' 

@d 
def hello2(): 
    print 'hello2 world !' 

@d('Applying it twice') 
@d('Would also work') 
def hello3(): 
    print 'hello3 world !' 

hello() 
hello2() 
hello3() 

# output 
# 
# This is working 
# hello world ! 
# my default message 
# hello2 world ! 
# Applying it twice 
# Would also work 
# hello3 world ! 

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

+0

बढ़िया, क्या जवाब है! – adkl

9

आप नामित तर्कों का उपयोग पर निर्भर कोई आपत्ति नहीं है, मैं आपको क्या चाहिए करने के लिए कुछ इसी तरह की:

def cached_property(method=None, get_attribute=lambda a: '_%s_cached' % (a,)): 
    """ 
    Caches an object's attribute. 

    Can be used in the following forms: 
    @cached_property 
    @cached_property() 
    @cached_property(get_attribute=lambda x: 'bla') 

    @param method: the method to memoizes 
    @param get_attribute: a callable that should return the cached attribute 
    @return a cached method 
    """ 
    def decorator(method): 
     def wrap(self): 
      private_attribute = get_attribute(method.__name__) 
      try: 
       return getattr(self, private_attribute) 
      except AttributeError: 
       setattr(self, private_attribute, method(self)) 
       return getattr(self, private_attribute) 
     return property(wrap) 
    if method: 
     # This was an actual decorator call, ex: @cached_property 
     return decorator(method) 
    else: 
     # This is a factory call, ex: @cached_property() 
     return decorator 

यह काम करता है क्योंकि केवल एक गैर कीवर्ड तर्क, समारोह सजाया डेकोरेटर को पारित कर दिया है।

ध्यान दें कि मैंने इस मामले में 'स्वयं' में सजाए गए फ़ंक्शन को पारित तर्कों का भी उपयोग किया था।

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