2012-07-02 20 views
5

किसी को अच्छी तरह से निम्नलिखित कोड की अंतिम पंक्ति व्याख्या कर सकते हैं:अजगर विधि परिभाषा का उपयोग डेकोरेटर

def myMethod(self): 
    # do something 

myMethod = transformMethod(myMethod) 

क्यों आप किसी अन्य विधि के माध्यम से एक विधि के लिए परिभाषा पास करना चाहते हैं? और यह कैसे काम करेगा? अग्रिम में धन्यवाद!

उत्तर

2

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

def wrapper(func): 
    def wrapped(): 
     print 'Enter' 
     result = func() 
     print 'Exit' 
     return result 
    return wrapped 

और यहाँ की आप कैसे कर सकते थे एक उदाहरण है:

यह इस तरह इस्तेमाल किया जा सकता का एक उदाहरण है, यह एक सरल आवरण जो सिर्फ प्रिंट 'Enter' और प्रत्येक कॉल पर 'बाहर निकलें' है इस का उपयोग करें:

>>> def say_hello(): 
...  print 'Hello' 
... 
>>> say_hello() # behavior before wrapping 
Hello 
>>> say_hello = wrapper(say_hello) 
>>> say_hello() # behavior after wrapping 
Enter 
Hello 
Exit 

सुविधा के लिए, अजगर decorator syntax जो सिर्फ इतना है कि समारोह परिभाषा समय में एक ही बात करता है समारोह रैपिंग की एक आशुलिपि संस्करण है प्रदान करता है, यहाँ है कि यह कैसे इस्तेमाल किया जा सकता है:

+०१२३५१६४१०
>>> @wrapper 
... def say_hello(): 
...  print 'Hello' 
... 
>>> say_hello() 
Enter 
Hello 
Exit 
+0

आपकी सभी मदद के लिए बहुत बहुत धन्यवाद! मैं बहुत सराहना करता हूं कि यह कितना स्पष्ट था। –

2

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

क्योंकि आप इसके व्यवहार को संशोधित करना चाहते हैं।

और यह कैसे काम करेगा?

पूरी तरह से, चूंकि फ़िथन में फ़ंक्शन प्रथम श्रेणी हैं।

def decorator(f): 
    def wrapper(*args, **kwargs): 
    print 'Before!' 
    res = f(*args, **kwargs) 
    print 'After!' 
    return res 
    return wrapper 

def somemethod(): 
    print 'During...' 

somemethod = decorator(somemethod) 

somemethod() 
+0

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

+2

"आप विधि के वास्तविक स्रोत कोड को अभी क्यों संशोधित नहीं करते?" क्योंकि आपके पास स्रोत कोड नहीं हो सकता है, या आपको इसे कई कार्यों में लागू करने की आवश्यकता है। –

+1

@ user1495015 या शायद जो संशोधन किया जाता है वह हमेशा समान होता है, वास्तव में जो वास्तव में संशोधित होता है उसका हिस्सा अलग होता है। इसके लिए, आप खुशी से सजावट का उपयोग कर सकते हैं। – glglgl

1

क्या आप का वर्णन एक डेकोरेटर, विधि/समारोह संशोधन का एक रूप है जो decorators के लिए विशेष सिंटेक्स के साथ बहुत आसान पूरा किया जा सकता है।

क्या आप का वर्णन

@transformMethod 
def myMethod(self): 
    # do something 

डेकोरेटर के बराबर है @staticmethod के रूप में, @classmethod, @functools.wraps(), में उदाहरण के लिए, बहुत मोटे तौर पर उपयोग किया जाता है @contextlib.contextmanager आदि आदि आदि

एक निश्चित अजगर के बाद से

संस्करण (मुझे लगता है कि यह 2.6 था), कक्षाओं को भी सजाया जा सकता है।

दोनों प्रकार के सजावटी खुशी से उन वस्तुओं को वापस करने की अनुमति देते हैं जो कार्य या कक्षाएं भी नहीं हैं। उदाहरण के लिए, आप जेनरेटर फ़ंक्शन को इस तरह से सजाने के लिए तैयार कर सकते हैं जो इसे एक ताना, एक सेट या जो कुछ भी बदल देता है।

apply = lambda tobeapplied: lambda f: tobeapplied(f()) 

@apply(dict) 
def mydict(): 
    yield 'key1', 'value1' 
    yield 'key2', 'value2' 
print mydict 

@apply(set) 
def myset(): 
    yield 1 
    yield 2 
    yield 1 
    yield 4 
    yield 2 
    yield 7 
print myset 

मैं यहां क्या करूँ?

मैं एक ऐसा फ़ंक्शन बनाता हूं जो "लागू होने वाली चीज़" लेता है और बदले में एक और फ़ंक्शन देता है।

यह "आंतरिक" फ़ंक्शन फ़ंक्शन को सजाए जाने के लिए लेता है, इसे कॉल करता है और इसके परिणाम को बाहरी फ़ंक्शन में डालता है और परिणाम देता है।

f() एक जनरेटर ऑब्जेक्ट देता है जिसे तब dict() या set() में डाल दिया जाता है।

+0

के लिए ज्ञापन एक बहुत आम उपयोग केस है, नहीं, यह * एक सजावटी है। '@' ठीक उसी ऑपरेशन के लिए सिंटैक्टिक चीनी है। –

+0

@ IgnacioVazquez-Abrams आप सही हैं। मैं पायथन की परिभाषा में बहुत करीब था। – glglgl

+0

@ IgnacioVazquez-Abrams हां, लेकिन अधिकांश लोग सजावटी को दूसरी तरफ देखते हैं। एक सामान्य विधि परिवर्तन के रूप में जरूरी नहीं है। –

0

आपको समझना होगा कि पायथन में, सब कुछ एक वस्तु है। एक समारोह एक वस्तु है। आप एक ही चीज को एक फ़ंक्शन ऑब्जेक्ट के साथ कर सकते हैं जो आप अन्य प्रकार की ऑब्जेक्ट्स के साथ कर सकते हैं: किसी सूची में स्टोर करें, एक डिक्शनरी में स्टोर करें, फ़ंक्शन कॉल से वापस आएं, आदि

आपके जैसे कोड के सामान्य कारण दिखाए गए अन्य फ़ंक्शन ऑब्जेक्ट को "लपेटना" है। उदाहरण के लिए, यहां एक रैपर है जो फ़ंक्शन द्वारा लौटाए गए मान को प्रिंट करता है।

def print_wrapper(fn): 
    def new_wrapped_fn(*args): 
     x = fn(*args) 
     print("return value of %s is: %s" % (fn.__name__, repr(x))) 
     return x 
    return new_wrapped_fn 

def test(a, b): 
    return a * b 

test = print_wrapper(test) 

test(2, 3) # prints: return value of test is: 6 

यह, इस तरह के एक उपयोगी कार्य, और इस तरह एक आम कार्य है अजगर इसके लिए विशेष समर्थन किया है। "पायथन सजावट" के लिए Google खोज।

0

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

def add_x_to_sequence(x, seq): 
    result = [] 
    for i in seq: 
     result.append(i + x) 
    return result 

def subtract_x_from_sequence(x, seq): 
    result = [] 
    for i in seq: 
     result.append(i - x) 
    return result 

अब, इन दोनों उदाहरण कार्यों कुछ खामियां है - वास्तविक जीवन में, उदाहरण के लिए, तो आप शायद सिर्फ पुनर्लेखन था सूची comprehensions के रूप में उन्हें - लेकिन पल के लिए स्पष्ट खामियों पर ध्यान न दें, और नाटक है कि हम उन्हें इस तरह से लिखते हैं, for छोरों दृश्यों से अधिक पुनरावृत्ति के रूप में होना चाहिए करते हैं। अब हम समस्या यह है कि हमारे दो कार्य लगभग एक ही बात कर सामना करते हैं, सिर्फ एक ही चाबी पल में अंतर होता है। इसका मतलब है कि हम खुद को दोहरा रहे हैं! और यह एक समस्या है। अब हमें कोड की अधिक लाइनों को बनाए रखना है, बग्स के लिए और अधिक जगह छोड़ना है, और दिखाई देने के बाद बग को छिपाने के लिए और अधिक जगह है।

इस समस्या का एक प्रमुख दृष्टिकोण एक समारोह बनाने के लिए लेता है कि एक समारोह है, और एक अनुक्रम में यह लागू होता है, इस तरह हो सकता है:

def my_map(func, x, seq): 
    result = [] 
    for i in seq: 
     result.append(func(i, x)) 
    return result 

अब सब हम क्या करना है विशिष्ट funcs परिभाषित है my_map को पारित करने के लिए (जो वास्तव में सिर्फ निर्मित map समारोह की एक विशेष संस्करण है)।

def sub(a, b): 
    return a - b 

def add(a, b): 
    return a + b 

और हम उन्हें इस तरह का उपयोग कर सकते हैं:

added = my_map(sub, x, seq) 

लेकिन इस दृष्टिकोण अपनी समस्या है। उदाहरण के लिए, हमारे मूल स्टैंड-अलोन फ़ंक्शंस से पढ़ने के लिए थोड़ा कठिन है; और हर बार जब हम जोड़ सकते हैं या मदों की एक सूची से x घटाना चाहते हैं, हम समारोह और तर्क के रूप में मूल्य निर्दिष्ट करना होगा। कि पठनीयता में सुधार होता है, और यह आसान समझने के लिए हमारे कोड में हो रहा है बनाने - हम इस एक बहुत कुछ कर रहे हैं, हम इसके बजाए एक ही समारोह ऐसा नाम है जो हमेशा एक ही कार्रवाई के लिए संदर्भित करता होगा। हम एक और समारोह में ऊपर लपेट सकता है ...

def add_x_to_sequence(x, seq): 
    return my_map(add, x, seq) 

लेकिन अब हम अपने आप को बार-बार दोहराने कर रहे हैं!और हम अपने नामस्थान को छेड़छाड़ करने, कार्यों का प्रसार भी कर रहे हैं।

डेकोरेटर इन समस्याओं से बाहर एक रास्ता प्रदान करते हैं। फ़ंक्शन को किसी अन्य फ़ंक्शन पर हर बार पर पास करने के बजाय, हम इसे एक बार पास कर सकते हैं।

def vectorize(func): 
    def wrapper(x, seq): 
     result = [] 
     for i in seq: 
      result.append(func(i, x)) 
     return result 
    return wrapper 

अब सब हम क्या करना है एक समारोह को परिभाषित करने और यह ऊपर के पास है, यह लपेटकर है:

def add_x_to_sequence(a, b): 
    return a + b 
add_x_to_sequence = vectorize(add_x_to_sequence) 

या, डेकोरेटर सिंटैक्स का उपयोग:

पहले हम एक आवरण समारोह को परिभाषित
@vectorize 
def add_x_to_sequence(a, b): 
    return a + b 

अब हम कई अलग अलग vectorize घ कार्यों में लिख सकते हैं, और उन्हें के सभी के लिए हमारे for तर्क सिर्फ एक में होता है pl इक्का। अब हमें अलग-अलग कार्यों को अलग-अलग ठीक या अनुकूलित करने की आवश्यकता नहीं है; हमारे सभी लूप-संबंधित बग और लूप से संबंधित अनुकूलन एक ही स्थान पर होते हैं; और हम अभी भी विशेष रूप से परिभाषित कार्यों के सभी पठनीयता लाभ प्राप्त करते हैं।

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