繁体   English   中英

告诉编译器它不知道的本地?

[英]Tell compiler about a local it doesn't know about?

我有一个名为let的函数,它修改调用命名空间以插入一个新变量。

def let(**nameValuePair):
    from inspect import stack

    name, value = nameValuePair.items()[0]
    stack()[1][0].f_locals[name] = value
    return value

这个想法是它允许你在任何你想要的地方插入赋值语句,即使Python中通常不允许赋值语句(尽管let()需要5个额外的字符。

从全局命名空间调用时,这非常有效。

>>> let(outside = 'World')
>>> print(outside)
World

这失败并出现错误NameError: global name 'hello' is not defined

>>> def breaker():
...     let(hello = 'World')
...     print(hello)
...
>>> breaker()

你可能会得出结论,问题在于我的let函数。 它不是 - 我的功能表现完美。 告诉你:

>>> def fine():
...     let(another = 'World')
...     print(locals()['another'])
...
>>> fine()
World

问题实际上来自于Python解释器编译breaker 为了演示,我将创建另一个函数,它与breaker做同样的事情,但使用普通赋值而不是let ,然后我将用dis反编译。

>>> def normal():
...     hello = 'World'
...     print(hello)
...
>>> normal()
World

>>> from dis import dis
>>> print(dis(normal))
 0 LOAD_CONST               1 ('World')
 3 STORE_FAST               0 (hello)
 6 LOAD_FAST                0 (hello)
 9 PRINT_ITEM
10 PRINT_NEWLINE
11 LOAD_CONST               0 (None)
14 RETURN_VALUE

>>> print(dis(breaker))
 0 LOAD_GLOBAL              0 (let)
 3 LOAD_CONST               1 ('hello')
 6 LOAD_CONST               2 ('World')
 9 CALL_FUNCTION          256
12 POP_TOP
13 LOAD_GLOBAL              2 (hello)  # <= This is the real problem.
16 PRINT_ITEM
17 PRINT_NEWLINE
18 LOAD_CONST               0 (None)
19 RETURN_VALUE

问题是,编译器正在查看breaker ,没有看到hello被分配到本地范围内的任何位置,因此假设它必须是全局变量。

我该怎么办? 当我的函数被调用时,我可以换掉我指出的有问题的行并将其替换为:

LOAD_GLOBAL 1 (locals)
CALL_FUNCTION 0
LOAD_CONST 1 ('hello')  # Or whatever the name of the variable is
BINARY_SUBSCR

我知道我可以通过简单地将变量存储在全局范围内来快速解决当前问题,但这可能会导致许多微妙的错误。 仅举几例:

  1. 全局变量被覆盖。
  2. 对象可能会在内存中持续存在的时间远远超过预期。
  3. 否则将是一个错字的东西是有效的。 (诚​​然,这个似乎不太可能)。

...实际上,如果我可以更改已编译的代码,我甚至可以在RETURN_VALUE之后添加STORE_GLOBALDELETE_GLOBAL 将前全局变量缓存到别处或其他什么?

locals()的文档说明如下:

注意不应修改此词典的内容; 更改可能不会影响解释器使用的本地和自由变量的值。

我检查过,并没有在inspect文档中看到关于f_locals的明确说明,但我不明白为什么会有任何不同。

简短的版本是,你做不到。 你不能在3.x中做到这一点,你不能在2.x的最新版本中做到这一点,如果你能够可靠地做到这一点我会感到惊讶。 您将来也无法做到这一点,因为局部变量太容易成为优化的目标。 一个编译器无法实现这一点,更不用说所有的python编译器了。

另一方面,也许还有另一种方法可以做你想做的事情。 那是什么?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM