如何修改Python中的本地命名空间

7 投票
7 回答
8863 浏览
提问于 2025-04-15 12:58

我想知道如何在Python中修改一个函数的本地命名空间。我知道在函数内部调用locals()可以返回这个函数的本地命名空间,但我想做一些类似的事情(我有理由这么做,因为g在f中是不可访问的,不过为了简单起见,我给了一个很简单、很傻的例子来说明这个问题):

def g():
   pass

def f():
    g()

f.add_to_locals({'g':g})

7 个回答

3

因为这个函数还没有被调用,所以它还没有“局部”栈帧。最简单的解决办法是使用一个全局的上下文:

handler = None
def f():
    handler()

def g(): pass

handler = g

或者你可以在函数对象上设置一个叫 g 的变量:

f.g = g

不过我不太确定怎么在函数内部获取这个函数对象。如果这是一个方法,你可以使用 self

3

你为什么不直接给f()加一个参数,然后把g()的引用传进去呢?

def g():
    pass

def f(func):
    func()

f(g)
10

你有几个选择。首先,注意到你例子中的 g 其实并不是函数内部的局部变量(也就是说,它并没有在函数内部被赋值),而是一个全局变量(也就是没有被赋值给局部变量)。这意味着在函数定义的模块中会去查找这个变量。这是个好消息,因为外部无法改变局部变量(除非你修改字节码),因为局部变量是在函数运行时被赋值的,而不是之前。

一个选择就是直接把你的函数放到函数所在模块的命名空间里。这种方法是可行的,但会影响到该模块中所有访问这个变量的函数,而不仅仅是你想要的那个函数。

如果你只想影响一个特定的函数,你需要把那个 func_globals 指向其他地方。可惜的是,这个属性是只读的,但你可以通过重新创建一个函数,保持相同的函数体,但使用不同的全局命名空间来实现你想要的效果:

import new
f = new.function(f.func_code, {'g': my_g_function}, f.func_name, f.func_defaults, f.func_closure)

现在 f 将是完全相同的,唯一的不同是它会在提供的字典中查找全局变量。注意,这样做会重新绑定整个全局命名空间——如果里面有 f 确实需要查找的变量,确保你也提供它们。不过,这种方法也有点黑科技,可能在除了 cpython 以外的其他 Python 版本上不太好使。

撰写回答