这是一种实现方法,它是通过侵入函数内部来创建一个“做正确的事”的新foo。(如@DSM所述)。不幸的是,我们不能直接进入
foo函数并弄乱它的内部,因为它们大多被标记为只读,因此我们要做的就是手动修改一个副本。
# Here's the original functiondef 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 itfoo()# Now lets override the bar functionimport types# This is our replacement functiondef my_bar(): print(" Woo hoo I'm the bar override")# This creates a new pre object used by our new foo function # based on the old foo functions pre object.foopre = types.CodeType( foo.func_pre.co_argcount, foo.func_pre.co_nlocals, foo.func_pre.co_stacksize, foo.func_pre.co_flags, foo.func_pre.co_pre, # This tuple is a new version of foo.func_pre.co_consts # NOTE: Don't get this wrong or you will crash python. ( foo.func_pre.co_consts[0], my_bar.func_pre, foo.func_pre.co_consts[2], foo.func_pre.co_consts[3], foo.func_pre.co_consts[4] ), foo.func_pre.co_names, foo.func_pre.co_varnames, foo.func_pre.co_filename, foo.func_pre.co_name, foo.func_pre.co_firstlineno, foo.func_pre.co_lnotab, foo.func_pre.co_freevars, foo.func_pre.co_cellvars )# This is the new function we're replacing foo with# using our new pre.foo = types.FunctionType( foopre , {})# Now use itfoo()我很确定它不会涵盖所有情况。但这适用于示例(对我而言,在旧的python 2.5.1上)
可能会整理一些丑陋的地方是:
- 庞大的参数列表传递给CodeType
- 由
co_consts
仅覆盖一个成员构成的丑陋的元组。所有信息都在co_consts中以确定要替换的内容-因此,更智能的功能可以做到这一点。我用手工挖了内部零件print( foo.func_pre.co_consts )
。
您可以使用解释器命令找到有关
CodeType和
FunctionType的信息
help( types.CodeType )。
更新:我认为这太难看了,所以我建立了一个辅助函数使其更漂亮。使用帮助程序,您可以编写:
# 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 worksfoo()
这是实现
monkey_patch_fn:
# 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_pre except AttributeError, e: pass return x original_pre = original_fn.func_pre new_consts = tuple( map( fix_consts, original_pre.co_consts ) ) pre_type_args = [ "co_argcount", "co_nlocals", "co_stacksize", "co_flags", "co_pre", "co_consts", "co_names", "co_varnames", "co_filename", "co_name", "co_firstlineno", "co_lnotab", "co_freevars", "co_cellvars" ] new_pre = types.CodeType( *[ ( getattr(original_pre,x) if x!="co_consts" else new_consts ) for x in pre_type_args ] ) return types.FunctionType( new_pre, {} )


