[英]Tell compiler about a local it doesn't know about?
I have a function called let
, which modifies the calling namespace to insert a new variable. 我有一个名为let
的函数,它修改调用命名空间以插入一个新变量。
def let(**nameValuePair):
from inspect import stack
name, value = nameValuePair.items()[0]
stack()[1][0].f_locals[name] = value
return value
The idea is it allows you insert assignment statements anywhere you want, even where assignment statements aren't typically allowed in Python (albeit, it requires 5 extra characters for let()
. 这个想法是它允许你在任何你想要的地方插入赋值语句,即使Python中通常不允许赋值语句(尽管let()
需要5个额外的字符。
This works perfectly when called from the global namespace. 从全局命名空间调用时,这非常有效。
>>> let(outside = 'World')
>>> print(outside)
World
This fails with the error NameError: global name 'hello' is not defined
: 这失败并出现错误NameError: global name 'hello' is not defined
:
>>> def breaker():
... let(hello = 'World')
... print(hello)
...
>>> breaker()
You might jump to the conclusion that the issue is with my let
function. 你可能会得出结论,问题在于我的let
函数。 It is not - my function performs perfectly. 它不是 - 我的功能表现完美。 To show you that: 告诉你:
>>> def fine():
... let(another = 'World')
... print(locals()['another'])
...
>>> fine()
World
The issue actually comes from when the Python interpreter compiles breaker
. 问题实际上来自于Python解释器编译breaker
。 To demonstrate, I'll create another function which does the same thing as breaker
, but using an ordinary assignment instead of let
, and then I'll decompile both with dis
. 为了演示,我将创建另一个函数,它与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
The problem is, the compiler is looking over breaker
, doesn't see that hello
gets assigned anywhere in the local scope, and so assumes that it must be a global variable. 问题是,编译器正在查看breaker
,没有看到hello
被分配到本地范围内的任何位置,因此假设它必须是全局变量。
What can I do about this? 我该怎么办? When my function gets called, could I, for example, swap out the problematic line that I indicated and replace it with: 当我的函数被调用时,我可以换掉我指出的有问题的行并将其替换为:
LOAD_GLOBAL 1 (locals)
CALL_FUNCTION 0
LOAD_CONST 1 ('hello') # Or whatever the name of the variable is
BINARY_SUBSCR
I know that I could quickly fix the immediate problem by simply storing the variable in the global scope, but that could lead to lots of subtle bugs. 我知道我可以通过简单地将变量存储在全局范围内来快速解决当前问题,但这可能会导致许多微妙的错误。 To name a few: 仅举几例:
... Actually, if I can change the compiled code, I could even add a STORE_GLOBAL
or DELETE_GLOBAL
after RETURN_VALUE
, perhaps? ...实际上,如果我可以更改已编译的代码,我甚至可以在RETURN_VALUE
之后添加STORE_GLOBAL
或DELETE_GLOBAL
? Cache the former global variable elsewhere or something? 将前全局变量缓存到别处或其他什么?
The documentation for locals()
notes the following: locals()
的文档说明如下:
Note The contents of this dictionary should not be modified; 注意不应修改此词典的内容; changes may not affect the values of local and free variables used by the interpreter. 更改可能不会影响解释器使用的本地和自由变量的值。
I checked and don't see an explicit note in the inspect
docs about f_locals
but I don't see why it would be any different. 我检查过,并没有在inspect
文档中看到关于f_locals
的明确说明,但我不明白为什么会有任何不同。
The short version is, you can't do that. 简短的版本是,你做不到。 You can't do it in 3.x, you couldn't do it in recent versions of 2.x, and I'd be surprised if you could ever reliably do it. 你不能在3.x中做到这一点,你不能在2.x的最新版本中做到这一点,如果你能够可靠地做到这一点我会感到惊讶。 You won't be able to do it in the future, either, because local variables are too easy a target for optimization. 您将来也无法做到这一点,因为局部变量太容易成为优化的目标。 There's no way one compiler would go for that, much less all the python compilers. 一个编译器无法实现这一点,更不用说所有的python编译器了。
On the other hand, maybe there's another way to do what you're trying to do. 另一方面,也许还有另一种方法可以做你想做的事情。 What is that? 那是什么?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.