2012-08-11 13 views
7

यदि मेरे पास यह फ़ंक्शन है, तो आंतरिक फ़ंक्शन को अपने स्वयं के कस्टम संस्करण के साथ बदलने के लिए मुझे क्या करना चाहिए?क्या नेस्टेड कार्यों के लिए ओवरराइड बराबर है?

def foo(): 
    def bar(): 
     # I want to change this 
     pass 

    # here starts a long list of functions I want to keep unchanged 
    def baz(): 
     pass 

कक्षाओं का उपयोग करके यह विधि को ओवरराइड करने में आसानी से किया जाएगा। हालांकि, मैं यह नहीं समझ सकता कि नेस्टेड कार्यों के साथ ऐसा कैसे किया जाए। एक वर्ग (या कुछ और) होने के लिए foo बदलना एक विकल्प नहीं है क्योंकि यह किसी दिए गए आयातित मॉड्यूल से आता है जिसे मैं संशोधित नहीं कर सकता।

+0

'bar' कहाँ प्रयोग किया जाता है:

# Use our function to get a new version of foo with "bar" replaced by mybar foo = monkey_patch_fn(foo, "bar", my_bar) # Check it works foo() 

यहाँ monkey_patch_fn के कार्यान्वयन है? क्या यह बाद के कार्यों में उपयोग किया जाता है (जैसे 'बाज़'?) आप * उन मामलों में इसे बदलना चाहते हैं, है ना? –

+0

बिल्कुल। ऐसा करने का कोई तरीका? – Paolo

+0

आप फ़ंक्शन के विभिन्न आंतरिक विवरणों तक पहुंच सकते हैं, क्योंकि वे केवल ऑब्जेक्ट्स हैं। अधिक जानने के लिए डीआईआर फ़ंक्शन और भाषा संदर्भ का उपयोग करें। – Marcin

उत्तर

10

यहाँ यह ऐसा करने का एक ही रास्ता है के रूप में यह में पारित कर सकते हैं, एक नया foo कि समारोह internals हैकिंग से "सही काम करता है" का निर्माण। (जैसा कि @DSM द्वारा उल्लिखित है)। दुर्भाग्यवश हम foo फ़ंक्शन और गड़बड़ी में अपने आंतरिक के साथ कूद नहीं सकते हैं, क्योंकि उन्हें अधिकतर केवल पढ़ने के लिए चिह्नित किया जाता है, इसलिए हमें जो करना है वह उस प्रतिलिपि को संशोधित करता है जिसे हम हाथ से बनाते हैं।

# Here's the original function 
def foo(): 
    def bar(): 
    print(" In bar orig") 
    def baz(): 
    print(" Calling bar from baz") 
    bar() 
    print("Foo calling bar:") 
    bar() 
    print("Foo calling baz:") 
    baz() 

# Here's using it 
foo() 

# Now lets override the bar function 

import types 

# This is our replacement function 
def my_bar(): 
    print(" Woo hoo I'm the bar override") 

# This creates a new code object used by our new foo function 
# based on the old foo functions code object. 
foocode = types.CodeType(
    foo.func_code.co_argcount, 
    foo.func_code.co_nlocals, 
    foo.func_code.co_stacksize, 
    foo.func_code.co_flags, 
    foo.func_code.co_code, 
    # This tuple is a new version of foo.func_code.co_consts 
    # NOTE: Don't get this wrong or you will crash python. 
    ( 
     foo.func_code.co_consts[0], 
     my_bar.func_code, 
     foo.func_code.co_consts[2], 
     foo.func_code.co_consts[3], 
     foo.func_code.co_consts[4] 
    ), 
    foo.func_code.co_names, 
    foo.func_code.co_varnames, 
    foo.func_code.co_filename, 
    foo.func_code.co_name, 
    foo.func_code.co_firstlineno, 
    foo.func_code.co_lnotab, 
    foo.func_code.co_freevars, 
    foo.func_code.co_cellvars) 

# This is the new function we're replacing foo with 
# using our new code. 
foo = types.FunctionType(foocode , {}) 

# Now use it 
foo() 

मुझे पूरा यकीन है कि यह सभी मामलों को पकड़ने वाला नहीं है।

  1. विशाल तर्क सूची CodeType को पारित किया जा रहा
  2. : लेकिन यह

    बदसूरत बिट्स है कि कुछ साफ कर रहे हैं के साथ कर सकता (एक पुराने अजगर 2.5.1 पर मेरे लिए) उदाहरण के लिए काम करता है बदसूरत tuple co_consts से केवल एक सदस्य ओवरराइडिंग से बनाया गया। सभी जानकारी को बदलने के लिए co_consts में है - ताकि एक स्मार्ट फ़ंक्शन ऐसा कर सके। मैंने print(foo.func_code.co_consts) का उपयोग करके हाथों से आंतरिक में खोद दिया।

आप दुभाषिया आदेश help(types.CodeType) का उपयोग करके CodeType और FunctionType बारे में कुछ जानकारी पा सकते हैं।

अद्यतन: मैंने सोचा कि यह बहुत बदसूरत था इसलिए मैंने इसे सुंदर बनाने के लिए एक सहायक कार्य बनाया। सहायक के साथ आप लिख सकते हैं:

# Returns a copy of original_fn with its internal function 
# called name replaced with new_fn. 
def monkey_patch_fn(original_fn, name, new_fn): 

    #Little helper function to pick out the correct constant 
    def fix_consts(x): 
    if x==None: return None 
    try: 
     if x.co_name == name: 
     return new_fn.func_code 
    except AttributeError, e: 
     pass 
    return x 

    original_code = original_fn.func_code 
    new_consts = tuple(map(fix_consts, original_code.co_consts)) 
    code_type_args = [ 
    "co_argcount", "co_nlocals", "co_stacksize", "co_flags", "co_code", 
    "co_consts", "co_names", "co_varnames", "co_filename", "co_name", 
    "co_firstlineno", "co_lnotab", "co_freevars", "co_cellvars" ] 

    new_code = types.CodeType(
    *[ (getattr(original_code,x) if x!="co_consts" else new_consts) 
     for x in code_type_args ]) 
    return types.FunctionType(new_code, {}) 
+0

इस उत्तर में दोनों कोडिंग दृष्टिकोण होने के बाद अंतर्दृष्टि दो बार प्रदान करता है। – MikeiLL

3

आप एक वैकल्पिक पैरामीटर

def foo(bar=None): 
    def _bar(): 
     # I want to change this 
     pass 
    if bar is None: 
     bar = _bar 
+0

मुझे यकीन नहीं है कि मैं समझता हूं। क्या आपका उत्तर दिए गए फ़ंक्शन को बदलने का संकेत नहीं देता है? शायद मैं स्पष्ट नहीं हूं, लेकिन मैं मूल 'foo' नहीं बदल सकता। बीटीडब्ल्यू, मैं अपने प्रश्न को थोड़ा सा संपादित करने जा रहा हूं। – Paolo

+4

मुझे लगता है कि ओपी कुछ प्रकार के बंदरगाह विकल्प की तलाश में है, क्योंकि 'foo'' किसी दिए गए आयातित मॉड्यूल से आता है जिसे मैं संशोधित नहीं कर सकता। " – PaulMcG

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