2009-07-22 10 views
44

चलो कहते हैं कि मैं निम्नलिखित करते हैं)।अजगर में एक समारोह से सज्जाकार पट्टी कैसे

spam दिया गया, मैं इसे सजावटी को कैसे हटा सकता हूं और अंतर्निहित "अनिश्चित" फ़ंक्शन प्राप्त कर सकता हूं?

उत्तर

30

सामान्य स्थिति में, आप क्योंकि

@with_connection 
def spam(connection): 
    # Do something 

जिसका अर्थ है कि "मूल" स्पैम भी अब मौजूद नहीं हो सकता है

def spam(connection): 
    # Do something 

spam = with_connection(spam) 

के बराबर है, नहीं कर सकता। एक (बहुत सुंदर नहीं) हैक इस होगा:

def with_connection(f): 
    def decorated(*args, **kwargs): 
     f(get_connection(...), *args, **kwargs) 
    decorated._original = f 
    return decorated 

@with_connection 
def spam(connection): 
    # Do something 

spam._original(testcon) # calls the undecorated function 
+0

यदि आप '_original' को कॉल करने के लिए कोड को संशोधित करने जा रहे हैं तो आप सजावटी से भी टिप्पणी कर सकते हैं। – eduffy

+2

@eduffy: यह सवाल का मुद्दा है। – balpha

+0

आप सही हैं .. मैंने परीक्षण के अर्थ में इसके बारे में नहीं सोचा था। – eduffy

14

देखो, FuglyHackThatWillWorkForYourExampleButICantPromiseAnythingElse:

orig_spam = spam.func_closure[0].cell_contents 

संपादित: कार्यों के लिए/तरीकों एक बार से और अधिक जटिल सज्जाकार आप उपयोग कर कोशिश कर सकते हैं के साथ और अधिक सजाया निम्नलिखित कोड। यह इस तथ्य पर निर्भर करता है कि सजाए गए कार्यों को मूल कार्य की तुलना में __name__d अलग-अलग हैं।

def search_for_orig(decorated, orig_name): 
    for obj in (c.cell_contents for c in decorated.__closure__): 
     if hasattr(obj, "__name__") and obj.__name__ == orig_name: 
      return obj 
     if hasattr(obj, "__closure__") and obj.__closure__: 
      found = search_for_orig(obj, orig_name) 
      if found: 
       return found 
    return None 

>>> search_for_orig(spam, "spam") 
<function spam at 0x027ACD70> 

हालांकि यह मूर्ख प्रमाण नहीं है। अगर सजावट से लौटाए गए समारोह का नाम सजाया गया है तो यह असफल हो जाएगा। हैट्टर() चेक का क्रम भी एक उदारवादी है, सजावट श्रृंखलाएं हैं जो किसी भी मामले में गलत परिणाम देती हैं।

+3

'func_closure' को 3.x में' __closure__' द्वारा प्रतिस्थापित किया जा रहा है और यह पहले से ही 2.6 –

+1

में है, मैंने देखा कि जब मैं फ़ंक्शंस के साथ खेल रहा था, लेकिन यदि आप किसी फ़ंक्शन पर एक से अधिक सजावट का उपयोग कर रहे हैं तो यह जटिल हो जाता है। आप '.func_closure [0] .cell_contents' को कॉल करते हुए 'सेल_कंटेंट्स नहीं है' तक कॉल करते हैं। मैं एक और अधिक सुरुचिपूर्ण समाधान की उम्मीद कर रहा था। – Herge

+0

शायद सजाया नहीं जाएगा, अगर सजावटी functools.wraps –

27

balpha समाधान इस मेटा-डेकोरेटर के साथ और अधिक generalizable बनाया जा सकता है:

def include_original(dec): 
    def meta_decorator(f): 
     decorated = dec(f) 
     decorated._original = f 
     return decorated 
    return meta_decorator 

तो फिर तुम @include_original के साथ अपने सज्जाकार को सजाने कर सकते हैं, और हर एक एक परीक्षण योग्य (असज्जित) संस्करण दूर यह अंदर tucked होगा।

@include_original 
def shout(f): 
    def _(): 
     string = f() 
     return string.upper() 
    return _ 



@shout 
def function(): 
    return "hello world" 

>>> print function() 
HELLO_WORLD 
>>> print function._original() 
hello world 
+4

का उपयोग करता है अब हम बात कर रहे हैं। मेटाडेकेशन एफटीडब्ल्यू। – brice

+1

क्या इसका विस्तार करने का कोई तरीका है ताकि गहरा स्तर मूल बाहरी सजाए गए फ़ंक्शन पर पहुंच योग्य हो, इसलिए मुझे तीन सजावट में लिपटे फ़ंक्शन के लिए ._original._original._original नहीं करना है? – Sparr

+0

@jcdyer क्या आपके सजावटी ___ का सटीक रूप से मतलब है? क्या मैं कुछ \ @include_original (अगली पंक्ति) \ @decorator_which_I_dont_control (अगली पंक्ति) function_definition की तरह कुछ कर सकता हूं? – Harshdeep

2

इस तरह के कार्यों का परीक्षण करने के लिए सामान्य दृष्टिकोण get_connection, विन्यास के रूप में किसी भी निर्भरता, बनाना है। फिर परीक्षण करते समय आप इसे नकली के साथ ओवरराइड कर सकते हैं। मूल रूप से जावा दुनिया में निर्भरता इंजेक्शन के समान ही है लेकिन पाइथन गतिशील प्रकृति के लिए बहुत आसान धन्यवाद। इसके लिए

कोड कुछ इस तरह दिख सकता है:

# decorator definition 
def with_connection(f): 
    def decorated(*args, **kwargs): 
     f(with_connection.connection_getter(), *args, **kwargs) 
    return decorated 

# normal configuration 
with_connection.connection_getter = lambda: get_connection(...) 

# inside testsuite setup override it 
with_connection.connection_getter = lambda: "a mock connection" 

अपने कोड के आधार पर आप पर कारखाने समारोह रहना डेकोरेटर तुलना में एक बेहतर वस्तु मिल सका। सजावट पर रखने के साथ यह मुद्दा यह है कि आपको इसे टायरडाउन विधि में पुराने मूल्य में बहाल करना याद रखना होगा।

6

के बजाय कर रही है ..

def with_connection(f): 
    def decorated(*args, **kwargs): 
     f(get_connection(...), *args, **kwargs) 
    return decorated 

@with_connection 
def spam(connection): 
    # Do something 

orig_spam = magic_hack_of_a_function(spam) 

तुम सिर्फ कर सकता है ..

def with_connection(f): 
    .... 

def spam_f(connection): 
    ... 

spam = with_connection(spam_f) 

..जो सभी @decorator वाक्य रचना करता है - आप तो स्पष्ट रूप से मूल spam_f एक do-कुछ भी नहीं डेकोरेटर का उपयोग कर सकते सामान्य रूप से

+0

अच्छा दृष्टिकोण, इतना चालाक! जीत के लिए – laike9m

1

जोड़ें: परिभाषित करने या with_connection आयात करने के बाद

def do_nothing(f): 
    return f 

लेकिन आप तरीकों कि इसका इस्तेमाल करने से पहले एक डेकोरेटर के रूप में, जोड़ें:

if TESTING: 
    with_connection = do_nothing 

तो अगर आप एक do-कुछ भी नहीं डेकोरेटर के साथ यह सच है, तो आप जगह ले ली है जाएगा with_connection करने के लिए वैश्विक परीक्षण निर्धारित किया है।

16

इस प्रश्न के लिए एक अद्यतन किया गया है। यदि आप पायथन 3 का उपयोग कर रहे हैं, तो आप __wrapped__ प्रॉपर्टी का उपयोग कर सकते हैं जो लपेटा हुआ फ़ंक्शन देता है।

यहाँ से Python Cookbook, 3rd edition

>>> @somedecorator 
>>> def add(x, y): 
...  return x + y 
... 
>>> orig_add = add.__wrapped__ 
>>> orig_add(3, 4) 
7 
>>> 

एक उदाहरण है कि विशेषता का अधिक विस्तृत उपयोग के लिए चर्चा देखें।

>>> from undecorated import undecorated 
>>> undecorated(spam) 

यह अलग सज्जाकार के सभी परतों के माध्यम से खुदाई जब तक यह नीचे समारोह तक पहुँच जाता है और मूल सज्जाकार बदलते आवश्यकता नहीं है की परेशानी के माध्यम से चला जाता है:

+0

पायथन 3! – funk

4

अब आप undecorated पैकेज का उपयोग कर सकते हैं। दोनों पायथन 2 और पायथन 3 पर काम करता है।

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