如何在python中修改本地命名空间

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。


你为什么不在f()上加一个论点,然后把一个引用传给g()呢?

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

但我不确定如何从函数本身中获取函数对象。如果这是一种方法,您将使用self


我假设您想这样做,因为函数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