चलो कहते हैं कि मैं निम्नलिखित करते हैं)।अजगर में एक समारोह से सज्जाकार पट्टी कैसे
spam
दिया गया, मैं इसे सजावटी को कैसे हटा सकता हूं और अंतर्निहित "अनिश्चित" फ़ंक्शन प्राप्त कर सकता हूं?
चलो कहते हैं कि मैं निम्नलिखित करते हैं)।अजगर में एक समारोह से सज्जाकार पट्टी कैसे
spam
दिया गया, मैं इसे सजावटी को कैसे हटा सकता हूं और अंतर्निहित "अनिश्चित" फ़ंक्शन प्राप्त कर सकता हूं?
सामान्य स्थिति में, आप क्योंकि
@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
देखो, 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>
हालांकि यह मूर्ख प्रमाण नहीं है। अगर सजावट से लौटाए गए समारोह का नाम सजाया गया है तो यह असफल हो जाएगा। हैट्टर() चेक का क्रम भी एक उदारवादी है, सजावट श्रृंखलाएं हैं जो किसी भी मामले में गलत परिणाम देती हैं।
'func_closure' को 3.x में' __closure__' द्वारा प्रतिस्थापित किया जा रहा है और यह पहले से ही 2.6 –
में है, मैंने देखा कि जब मैं फ़ंक्शंस के साथ खेल रहा था, लेकिन यदि आप किसी फ़ंक्शन पर एक से अधिक सजावट का उपयोग कर रहे हैं तो यह जटिल हो जाता है। आप '.func_closure [0] .cell_contents' को कॉल करते हुए 'सेल_कंटेंट्स नहीं है' तक कॉल करते हैं। मैं एक और अधिक सुरुचिपूर्ण समाधान की उम्मीद कर रहा था। – Herge
शायद सजाया नहीं जाएगा, अगर सजावटी functools.wraps –
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
का उपयोग करता है अब हम बात कर रहे हैं। मेटाडेकेशन एफटीडब्ल्यू। – brice
क्या इसका विस्तार करने का कोई तरीका है ताकि गहरा स्तर मूल बाहरी सजाए गए फ़ंक्शन पर पहुंच योग्य हो, इसलिए मुझे तीन सजावट में लिपटे फ़ंक्शन के लिए ._original._original._original नहीं करना है? – Sparr
@jcdyer क्या आपके सजावटी ___ का सटीक रूप से मतलब है? क्या मैं कुछ \ @include_original (अगली पंक्ति) \ @decorator_which_I_dont_control (अगली पंक्ति) function_definition की तरह कुछ कर सकता हूं? – Harshdeep
इस तरह के कार्यों का परीक्षण करने के लिए सामान्य दृष्टिकोण 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"
अपने कोड के आधार पर आप पर कारखाने समारोह रहना डेकोरेटर तुलना में एक बेहतर वस्तु मिल सका। सजावट पर रखने के साथ यह मुद्दा यह है कि आपको इसे टायरडाउन विधि में पुराने मूल्य में बहाल करना याद रखना होगा।
के बजाय कर रही है ..
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-कुछ भी नहीं डेकोरेटर का उपयोग कर सकते सामान्य रूप से
अच्छा दृष्टिकोण, इतना चालाक! जीत के लिए – laike9m
जोड़ें: परिभाषित करने या with_connection आयात करने के बाद
def do_nothing(f):
return f
लेकिन आप तरीकों कि इसका इस्तेमाल करने से पहले एक डेकोरेटर के रूप में, जोड़ें:
if TESTING:
with_connection = do_nothing
तो अगर आप एक do-कुछ भी नहीं डेकोरेटर के साथ यह सच है, तो आप जगह ले ली है जाएगा with_connection करने के लिए वैश्विक परीक्षण निर्धारित किया है।
इस प्रश्न के लिए एक अद्यतन किया गया है। यदि आप पायथन 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)
यह अलग सज्जाकार के सभी परतों के माध्यम से खुदाई जब तक यह नीचे समारोह तक पहुँच जाता है और मूल सज्जाकार बदलते आवश्यकता नहीं है की परेशानी के माध्यम से चला जाता है:
पायथन 3! – funk
अब आप undecorated पैकेज का उपयोग कर सकते हैं। दोनों पायथन 2 और पायथन 3 पर काम करता है।
यदि आप '_original' को कॉल करने के लिए कोड को संशोधित करने जा रहे हैं तो आप सजावटी से भी टिप्पणी कर सकते हैं। – eduffy
@eduffy: यह सवाल का मुद्दा है। – balpha
आप सही हैं .. मैंने परीक्षण के अर्थ में इसके बारे में नहीं सोचा था। – eduffy