【问题标题】:How to modify the local namespace in python如何在python中修改本地命名空间
【发布时间】:2010-11-11 15:22:35
【问题描述】:

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

def g():
   pass

def f():
    g()

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

【问题讨论】:

  • 你使用这种奇怪的技术的最终目标是什么?为什么不能在 f() 之前定义 g()?
  • 我之前曾尝试在运行时弄乱函数的命名空间(用于测试/模拟)但失败了......仅供参考。
  • add_to_locals 到底是什么?对我来说似乎不存在。 AttributeError: 'function' object has no attribute 'add_to_locals'
  • @CharlieParker 它不存在。这就是他正在尝试实施的/询问是否有解决方案来做到这一点。
  • 你设法让它工作了吗?

标签: python namespaces local


【解决方案1】:

您有几个选择。首先,请注意,您的示例中的 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 版本。

【讨论】:

  • 我不知道这是否是“好”的代码,但这正是我想要的。我正在将一个代码库转换为另一个代码库,并且我希望能够模拟另一种语言的语言构造。另一种语言可以打包代码并在命名空间中添加或删除该代码。它在覆盖不同的功能时很有用。所以它以类似方式的堆栈操作命名空间。这是我能找到的最接近的东西,可以让我有效地做到这一点。谢谢
  • 这是什么new 库?
【解决方案2】:

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

handler = None
def f():
    handler()

def g(): pass

handler = g

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

f.g = g

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

【讨论】:

  • 如果你真的想在函数内部使用函数对象,你可以使用inspect: import inspect import pprint def fn(): frame = inspect.currentframe() print frame.f_globals[frame.f_code.co_name].g fn.g = 'hello' fn()
  • 对于类函数、嵌套函数/闭包和 lambdas 以及任何修饰的东西都被破坏了——它将返回装饰器的包装函数,而不是实际的函数。
  • 如果f 是类/实例方法怎么办?
【解决方案3】:

为什么不直接向f() 添加一个参数并传递一个对g() 的引用?

def g():
    pass

def f(func):
    func()

f(g)

【讨论】:

    【解决方案4】:

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

    def g():
       print "g"
    
    def f():
        f.g()
    
    f.g = g
    

    【讨论】:

    • 这只适用于全局函数;没有办法从 f 中可靠地访问 f。
    • 你能扩展一下吗? Python 函数是对象,本地或全局没有区别 - 除非您的意思是 methods 但这是完全不同的故事,而不是 OP 所要求的。
    • 如果f 是类/实例方法怎么办?
    【解决方案5】:

    我假设你想这样做,因为函数 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(),该版本可以执行您想要的操作。

    【讨论】:

      【解决方案6】:

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

      不过,你可以这样做:

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

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

      【讨论】:

      • 这个网站应该真的需要解释 -1 票,所以人们不能无缘无故地匿名投票否决正确的答案。
      【解决方案7】:

      这似乎有效

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

      【讨论】:

      • 这在函数内部不起作用。它不会将值添加到命名空间。
      猜你喜欢
      • 1970-01-01
      • 2016-11-05
      • 1970-01-01
      • 2010-12-05
      • 1970-01-01
      • 2011-08-29
      • 2012-10-10
      • 2014-08-25
      • 1970-01-01
      相关资源
      最近更新 更多