嵌套函数是否存在等价的覆盖? [英] Does an equivalent of override exist for nested functions?

查看:86
本文介绍了嵌套函数是否存在等价的覆盖?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有这个函数,我应该如何用自己的定制版本替换内部函数?

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 函数并且混淆了它的内部函数,因为它们大多被标记为只读,所以我们必须做的是修改我们构造的副本
$ b

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:


  1. 传递给CodeType的巨大参数列表

  2. co_consts 仅覆盖一个成员。所有的信息都在co_consts中以确定要替换哪个 - 所以更聪明的功能可以做到这一点。我用 print(foo.func_code.co_consts)手工挖入内部。

  1. The huge argument list being passed to CodeType
  2. 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 using print( foo.func_code.co_consts ).

您可以使用解释器
命令<$>找到关于 CodeType FunctionType 的一些信息c $ c> help(types.CodeType)。

You can find some information about the CodeType and FunctionType by using the interpreter command help( types.CodeType ).

UPDATE:
我觉得这太难看了,所以我建了一个帮手功能使其更漂亮。使用助手,您可以编写:

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, {} )

这篇关于嵌套函数是否存在等价的覆盖?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆