在Python中更改运行时对函数的引用

jpc*_*cgt 5 python bytecode introspection

我需要在运行时更改对另一个函数内的函数的调用.

请考虑以下代码:

def now():
    print "Hello World!"

class Sim:
    def __init__(self, arg, msg):
        self.msg = msg
        self.func = arg
        self.patch(self.func)

    def now(self):
        print self.msg

    def run(self):
        self.func()

    def patch(self, func):
        # Any references to the global now() in func
        # are replaced with the self.now() method.

def myfunc():
    now()
Run Code Online (Sandbox Code Playgroud)

然后 ...

>>> a = Sim(myfunc, "Hello Locals #1")
>>> b = Sim(myfunc, "Hello Locals #2")
>>> b.run()
Hello Locals #2
>>> a.run()
Hello Locals #1
Run Code Online (Sandbox Code Playgroud)

一个用户编写代码,myfunc()调用全局定义的函数now(),我无法编辑它.但我想让它调用Sim实例的方法.所以我需要myfunc()在运行时"修补"该功能.

我怎么能这样做?

一种可能的解决方案是编辑字节码,如下所示:http://web.archive.org/web/20140306210310/http: //www.jonathon-vogel.com/posts/patching_function_bytecode_with_python但我想知道是否有更方便.

kin*_*all 3

这比看起来更棘手。为此,您需要:

  • 使用方法创建一个dict子类__missing__来保存新值now。然后该__missing__方法从通常的字典中获取字典中没有的任何项目globals()

  • 从现有myfunc()函数创建一个新函数对象,保留其代码对象,但使用为全局变量创建的新字典。

  • 使用新函数的函数名称将新函数分配回全局变量。

就是这样:

def now():
    print "Hello World!"

class NowGlobals(dict):
    def __init__(self, now, globals):
        self["now"] = now
        self.globals = globals
    def __missing__(self, key):
        return self.globals[key]

class Sim(object):
    def __init__(self, func):
        func = self.patch(func)
        self.func = func
    def now(self):
        print "Hello locals!"
    def patch(self, func):
        funcname   = func.__name__
        nowglobals = NowGlobals(self.now, func.func_globals)
        func = type(func)(func.func_code, nowglobals)
        globals()[funcname] = func
        return func

def myfunc():
    now()

sim = Sim(myfunc)
myfunc()
Run Code Online (Sandbox Code Playgroud)

确实没有必要在课堂上使用它,但我一直保持这种方式,因为这就是你最初编写它的方式。

如果myfunc在另一个模块中,您需要重写Sim.patch()以修补回该名称空间,例如module.myfunc = sim.patch(module.myfunc)