Does an equivalent of override exist for nested functions?(嵌套函数是否存在等效的覆盖?)
问题描述
如果我有这个功能,我该怎么用我自己的自定义版本替换内部功能?
If I have this function, what should I do to replace the inner function with my own custom version?
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
更改为类(或其他任何东西)不是一种选择,因为它来自我无法修改的给定导入模块.
Using classes this would be easily done overriding the method. Though, I can't figure out how to do that with nested functions. Changing foo
to be a class (or anything else) is not an option because it comes from a given imported module I can't modify.
推荐答案
这里有一种方法,通过破解函数内部来创建一个做正确的事"的新 foo.(正如@DSM 所提到的).不幸的是,我们不能直接跳到 foo
函数并弄乱它的内部结构,因为它们大多被标记为只读,所以我们要做的是修改我们手动构建的副本.
Here's one way of doing it, creating a new foo that "does the right thing" by hacking the function internals. ( As mentioned by @DSM ). Unfortunately we cant just jump into the foo
function and mess with its internals, as they're mostly marked read only, so what we have to do is modify a copy we construct by hand.
# 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()
我很确定它不会捕获所有案例.但它适用于示例(对我来说是旧的 python 2.5.1 )
I'm pretty sure its not going to catch all cases. But it works for the example (for me on an old python 2.5.1 )
可以进行一些整理的丑陋部分是:
Ugly bits that could do with some tidy up are:
- 传递给 CodeType 的巨大参数列表
- 由
co_consts
构造的丑陋元组,仅覆盖一个成员.所有信息都在 co_consts 中以确定要替换哪个 - 因此更智能的函数可以做到这一点.我使用print( foo.func_code.co_consts )
手工挖掘内部结构.
- The huge argument list being passed to CodeType
- The ugly tuple constructed from
co_consts
overriding only one member. All the info is in co_consts to determine which to replace - so a smarter function could do this. I dug into the internals by hand usingprint( foo.func_code.co_consts )
.
你可以通过解释器找到一些关于CodeType
和FunctionType
的信息命令 help( types.CodeType )
.
You can find some information about the CodeType
and FunctionType
by using the interpreter
command help( types.CodeType )
.
更新:我觉得这太丑了,所以我构建了一个辅助函数来使它更漂亮.使用助手,您可以编写:
UPDATE: I thought this was too ugly so I built a helper function to make it prettier. With the helper you can write:
# 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
的实现:
# 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, {} )
这篇关于嵌套函数是否存在等效的覆盖?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!