2012-08-11 2 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

을 사용 :

여기
# 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의 구현인가? 나중의 함수 (예 :'baz')에 사용됩니까? 당신은 * 그러한 경우에 그것을 바꿀 것입니다. 맞습니까? –

+0

정확히. 어떻게 할 수 있니? – Paolo

+0

functione의 다양한 내부 다이어그램은 객체 일 뿐이므로 액세스 할 수 있습니다. 자세한 내용은 dir 함수 및 언어 참조를 사용하십시오. – Marcin

답변

10

여기에 그 일을 한 가지 방법이기로에 전달할 수 있습니다. (@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에 ​​나를 위해) 예 작동 co_consts에서 생성 된 못생긴 튜플은 단 하나의 멤버를 무시합니다. 모든 정보는 co_consts로 대체 할 항목을 결정하므로 더 똑똑한 기능이이를 수행 할 수 있습니다. print(foo.func_code.co_consts)을 사용하여 손으로 내부를 파 냈습니다.

당신은 인터프리터에게 명령 help(types.CodeType)을 사용하여 CodeTypeFunctionType에 대한 몇 가지 정보를 찾을 수 있습니다.

업데이트 : 나는 이것이 너무 못 생겨서 내가 더 예쁘게 만드는 도우미 기능을 만들었다 고 생각했습니다. 도우미로 당신은 쓸 수 있습니다 :`bar`이

# 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

당신은 함수 내부를 해킹 "옳은 일을한다"고 새로운 foo는 생성, 선택적 매개 변수

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

나는 내가 이해하고 있는지 잘 모르겠다. 당신의 대답은 주어진 기능을 바꾸는 것을 의미하지 않습니까? 어쩌면 나는 명확하지 않았지만 원래 'foo'를 바꿀 수는 없습니다. Btw, 제 질문을 조금 편집 할 것입니다. – Paolo

+4

OP가 일종의 monkeypatch 옵션을 찾고 있다고 생각합니다.'foo'는 "내가 수정할 수없는 주어진 임포트 된 모듈에서 온 것입니다." – PaulMcG

관련 문제