【问题标题】:Any way to modify locals dictionary?有什么办法可以修改本地人字典吗?
【发布时间】:2010-11-29 20:00:10
【问题描述】:

locals 是一个返回本地值字典的内置函数。文档说:

警告

这本词典的内容应该 不可修改;更改可能不会 影响局部变量的值 由解释器使用。

不幸的是,exec 在 Python 3.0 中也存在同样的问题。有没有办法解决这个问题?

用例

考虑:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

depends 将其参数中提供的字符串存储在列表test.dependences 中。这些字符串是字典d 中的键。我希望能够编写put_into_locals,以便我们可以将值从d 中提取出来并将它们放入本地。这可能吗?

【问题讨论】:

  • 为什么test. dependencies = ["a", "b", "c", "d", "e", "f"] 起作用,然后将我上面写的作业装饰到你的test() 函数中?
  • 您是否设法更新/修改本地人?
  • 有没有办法让它适用于 python 3 或更高版本?

标签: python local-variables


【解决方案1】:

我刚刚测试了 exec,它适用于 Python 2.6.2

>>> def test():
...     exec "a = 5"
...     print a
...
>>> test()
5

如果您使用的是 Python 3.x,它不再起作用,因为局部变量在运行时被优化为数组,而不是使用字典。

当 Python 检测到“exec 语句”时,它会强制 Python 将本地存储从数组切换到字典。然而,由于“exec”是 Python 3.x 中的一个函数,编译器无法做出这种区分,因为用户可以执行“exec = 123”之类的操作。

http://bugs.python.org/issue4831

修改函数的局部变量 没有苍蝇是不可能的 几个后果:通常, 函数局部变量不存储在 字典,而是一个数组,其 索引在编译时确定 从已知的语言环境。这碰撞 至少添加了新的本地人 执行。旧的 exec 语句 规避了这一点,因为 编译器知道,如果一个 exec 没有 全局/本地参数发生在 函数,该命名空间将是 “未优化”,即不使用 当地人数组。由于 exec() 现在是 正常功能,编译器不会 知道“exec”可能绑定到什么,并且 所以不能特殊对待。

【讨论】:

  • 我认为这是不可能的,这是非常确定的
  • @Casebash,这可能是可能的,它只需要字节码黑客或 Python 2.x
  • @Casebash:你可能不想屏住呼吸。 Python 字节码没有很好的文档记录。
  • 我可能有一天会自己看看。 ATM,我真的不会从中获得足够的效用来证明努力的合理性
  • 问题不在于解释器在exec=123 之后会不必要地退缩优化;即使在print=eval 之后,它也会信任看似无辜的print("hello=world")
【解决方案2】:

局部变量由赋值语句修改。

如果您有字符串形式的字典键,请不要同时将它们设为局部变量 - 只需将它们用作字典键即可。

如果您绝对必须有局部变量,请执行此操作。

def aFunction( a, b, c, d, e, f ):
    # use a, b, c, d, e and f as local variables

aFunction( **someDictWithKeys_a_b_c_d_e_f )

这将从你的字典中填充一些局部变量,而不会做任何神奇的事情。

【讨论】:

  • 这是一个有趣的想法。但是,在许多应用程序中,字典实际上包含许多其他变量(aFunction() 不需要这些变量),这使得 aFunction() 的当前定义中断。一个有用的概括是:aFunction(a, b, c, d, e, f, **kwargs).
  • @S. Lott:让我重新表述一下我的观点:当someDictWithKeys_a_b_c_d_e_f 包含比这几个变量更多的键时,签名def aFunction(a, b, c, d, e, f) 会中断,这是执行复杂科学计算时的典型情况(整个计算使用的变量比大多数它调用的函数)。正如我所指出的,def aFunction(a, b, c, d, e, f, **kwargs) 是解决这种情况的一种便捷方式。
  • @S. Lott:只需使用aFunction(**{'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6}) 运行您的答案代码(我赞成),这将准确地说明为什么我的评论对 StackOverflow 读者有用。在现实生活中的科学计算中,字典包含的变量通常比单独发送到每个调用的函数的变量要多。
  • @S. Lott:这样做的全部目的是为了更方便地访问 dict 或 object 成员。这个想法是,对于某些n,像(a**2+b)*exp(c*d/e) 这样的公式比(n.a**2+n.b)*exp(n.c*n.d/n.e) 更容易阅读(也许只是包装包含相关变量的dict)。至于“糟糕的设计”,假设您有一些参数决定了函数集合的形状。例如,这些可以是描述气体状态方程和其他属性的材料参数。
  • 每个函数都使用参数的一个子集,您不想编写一堆样板来仅提取真正需要的部分。当函数需要更多参数时,你不想修改调用者,当模型的不同部分需要引入新参数时,你不想修改内部函数。
【解决方案3】:

这是不可能的。我认为这是为了以后进行性能优化。 Python 字节码通过索引而不是名称来引用本地人;如果要求 locals() 是可写的,它可能会阻止解释器实现一些优化,或者使它们变得更加困难。

我相当肯定你不会找到任何核心 API 来保证你可以像这样编辑本地,因为如果那个 API 可以做到,locals() 也不会有这个限制。

不要忘记所有本地变量必须在编译时存在;如果您引用的名称在编译时未绑定到本地名称,则编译器会假定它是全局名称。编译后不能“创建”本地变量。

请参阅this question 了解一种可能的解决方案,但这是一种严重的黑客攻击,您真的不想这样做。

请注意,您的示例代码存在一个基本问题:

@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

"test.dependencies" 不是指“f.dependencies”,其中 f 是当前函数;它引用了实际的全局值“test”。这意味着如果您使用多个装饰器:

@memoize
@depends("a", "b", "c", "d", "e", "f")
def test():
    put_into_locals(test.dependencies)

它将不再起作用,因为“test”是 memoize 的包装函数,而不是依赖的。 Python真的需要一种方法来引用“当前正在执行的函数”(和类)。

【讨论】:

    【解决方案4】:

    我会将它存储在一个变量中:

    refs    = locals()
    def set_pets():
        global refs
        animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
        for i in range(len(animals)):
            refs['pet_0%s' % i] = animals[i]
    
    set_pets()
    refs['pet_05']='bird'
    print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05 )
    >> dog fish monkey cat fox bird
    

    如果你想在将它放入 locals() 之前测试你的 dict:

    def set_pets():
        global refs
        sandbox = {}
        animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
        for i in range(len(animals)):
            sandbox['pet_0%s' % i] = animals[i]
        # Test sandboxed dict here
        refs.update( sandbox )
    

    MacOS Sierra 上的 Python 3.6.1

    【讨论】:

      【解决方案5】:

      我不确定它是否受到相同的限制,但您可以通过检查模块获得对当前帧(以及从那里,局部变量字典)的直接引用:

      >>> import inspect
      >>> inspect.currentframe().f_locals['foo'] = 'bar'
      >>> dir()
      ['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect']
      >>> foo
      'bar'
      

      【讨论】:

      • 这和locals()完全一样; inspect.currentframe().f_locals is locals() 是真的。
      • 这并没有完全错误,但它只适用于框架是最顶层的框架,即全局范围。它不会在本地范围内工作。
      猜你喜欢
      • 2018-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-06
      • 2013-09-04
      • 1970-01-01
      相关资源
      最近更新 更多