How to modify the local namespace in python
如何在Python中修改函数的本地命名空间?我知道local s()在函数内部调用时返回函数的本地命名空间,但我想做类似的事情(我有一个理由要这样做,因为g对f是不可访问的,但给出一个简单、愚蠢的示例来说明问题要快些):
1 2 3 4 5 6 7 | def g(): pass def f(): g() f.add_to_locals({'g':g}) |
你有几个选择。首先,请注意,在您的示例中,g实际上不是函数的局部变量(即,它不是在函数中分配的),而是全局变量(即,尚未分配给局部变量)。这意味着它将在定义函数的模块中查找。这是幸运的,因为没有办法从外部改变局部变量(除了修补字节码),因为它们是在函数运行时被分配的,而不是以前。
一个选项是简单地将函数注入到函数的模块的名称空间中。这将有效,但会影响该模块中访问变量的每个函数,而不仅仅是一个函数。
要只影响一个函数,您需要将func_global指向其他地方。不幸的是,这是一个只读属性,但您可以通过使用相同的主体(但全局命名空间不同)重新创建函数来执行所需的操作:
1 2 | import new f = new.function(f.func_code, {'g': my_g_function}, f.func_name, f.func_defaults, f.func_closure) |
现在,f将是缩进的,除了它将在提供的dict中查找全局变量之外。请注意,这将重新绑定整个全局命名空间-如果其中有f查找的变量,请确保您也提供它们。不过,这也相当简单,除了cpython之外,可能不适用于其他版本的python。
你为什么不在
1 2 3 4 5 6 7 | def g(): pass def f(func): func() f(g) |
由于函数没有被调用,它还没有"本地"堆栈帧。最简单的解决方案是使用全局上下文:
1 2 3 4 5 6 7 | handler = None def f(): handler() def g(): pass handler = g |
或者可以在函数对象上设置g:
1 | f.g = g |
但我不确定如何从函数本身中获取函数对象。如果这是一种方法,您将使用
我假设您想这样做,因为函数f不是由您定义的,而是由其他模块定义的。所以您需要更改f()的工作方式。尤其是,要更改调用g时所调用的内容。
所以我建议:
1 2 3 4 5 6 | import thirdpartypackage def mynewg(): pass thirdpartypackage.g = mynewg |
这将更改模块第三方包的全局G。所以当现在调用thirdpartypackage.f()时,它将调用mynewg()而不是g()。
如果这不能解决问题,可能g()实际上是从with in g f()或somthing导入的。那么解决方案是:
1 2 3 4 5 6 7 8 9 | import thirdpartypackage def mynewg(): pass deg mynewf(): mynewg() thirdpartypackage.f = mynewf |
也就是说,您可以使用一个修改过的版本完全重写f(),该版本执行您想要的操作。
我认为你可以从一个完全不同的角度来解决这个问题。函数是带有字典的对象;因此,可以将g添加到f,并使用它:
1 2 3 4 5 6 7 | def g(): print"g" def f(): f.g() f.g = g |
不执行的函数没有任何局部变量;本地上下文是在运行函数时创建的,在函数退出时销毁,因此没有"本地命名空间"可从函数外部进行修改。
不过,您可以这样做:
1 2 3 4 5 6 7 8 9 10 | def f(): g = [1] def func(): print g[0] return func, g f, val = f() f() val[0] = 2 f() |
这将使用数组来模拟引用。
这个好像管用
1 2 3 4 5 | def add_to_locals(l): l['newlocal'] = 1 add_to_locals(locals()) assert newlocal |