6

如何在 python 中修改函数的本地命名空间?我知道 locals() 在函数内部调用时会返回函数的本地命名空间,但我想做这样的事情(我有一个原因,为什么我想在 f 无法访问 g 的情况下这样做,但给出更快一个简单而愚蠢的例子来说明问题):

def g():
   pass

def f():
    g()

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

7 回答 7

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 现在将是相同的,除了它将在提供的 dict 中查找全局变量。请注意,这会重新绑定整个全局命名空间 - 如果 f确实查找了变量,请确保您也提供它们。不过,这也相当hacky,并且可能不适用于cpython以外的python版本。

于 2009-07-17T17:07:52.630 回答
3

你为什么不只是添加一个参数f()并传递一个引用g()呢?

def g():
    pass

def f(func):
    func()

f(g)
于 2009-07-17T09:29:45.320 回答
3

由于没有调用该函数,因此它还没有“本地”堆栈帧。最简单的解决方案是使用全局上下文:

handler = None
def f():
    handler()

def g(): pass

handler = g

或者你可以在函数对象上设置 g :

f.g = g

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

于 2009-07-17T08:54:45.843 回答
2

我认为您可以从完全不同的角度解决问题。
函数是对象,有它们的字典;因此,您可以将 g 添加到 f,并使用它:

def g():
   print "g"

def f():
    f.g()

f.g = g
于 2009-07-17T09:24:05.533 回答
2

我假设您想这样做,因为函数 f 不是由您定义的,而是由其他模块定义的。所以你想改变 f() 的工作方式。特别是,您想更改调用 g 时调用的内容。

所以我会建议这个:

import thirdpartypackage

def mynewg():
   pass

thirdpartypackage.g = mynewg

这将更改模块thirdpartypackage 的全局g。因此,当现在调用thirdpartypackage.f() 时,它将调用mynewg() 而不是g()。

如果这不能解决它,也许 g() 实际上是从 withing f() 或其他东西导入的。那么解决方案是这样的:

import thirdpartypackage

def mynewg():
   pass

deg mynewf():
   mynewg()

thirdpartypackage.f = mynewf

也就是说,您使用修改后的版本完全覆盖 f(),该版本可以执行您想要的操作。

于 2009-07-17T10:03:47.513 回答
1

未执行的函数没有任何局部变量;本地上下文在您运行函数时创建,并在它退出时销毁,因此没有“本地名称空间”可以从函数外部修改。

不过,您可以执行以下操作:

def f():
    g = [1]
    def func():
        print g[0]
    return func, g

f, val = f()
f()
val[0] = 2
f()

这使用一个数组来模拟一个引用。

于 2009-07-17T08:56:19.943 回答
0

这似乎有效

def add_to_locals(l):
    l['newlocal'] = 1

add_to_locals(locals())
assert newlocal
于 2012-01-05T11:12:52.263 回答