简体   繁体   English

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

[英]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: 仅举几例:

  1. Global variables get written over. 全局变量被覆盖。
  2. Objects might persist in memory for far longer than intended. 对象可能会在内存中持续存在的时间远远超过预期。
  3. Something that would be a typo otherwise, is instead valid. 否则将是一个错字的东西是有效的。 (admittedly this one seems quite unlikely). (诚​​然,这个似乎不太可能)。

... 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_GLOBALDELETE_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.

相关问题 告诉 Flask-Migrate / Alembic 不要删除它不知道的任何表 - Tell Flask-Migrate / Alembic to NOT drop any tables it doesn't know about Python 不知道俄语字母 - Python doesn't know about russian letters 如何设置不了解系统软件包的virtualenv? - How can I set up a virtualenv that doesn't know about system packages? 被毁的对话框无法正确显示 - Destroyed about dialog doesn't reappear properly 导入的 class 不知道局部变量 - Imported class doesn't know local variables 我不知道如何处理消失的 python 模块 - I don't know what do about about a module of python that dissapered Python:类型错误,但我不知道要更改什么! 对 Smtplib 了解很多的人的问题 - Python: Type Error, but I don't know what to change! Question for Someone who know many about Smtplib 为什么我的变量值没有更新? 谢谢你的帮助,我有一段时间没有编码了,我不知道你在说什么,所以谢谢 - Why doesn't my variable value get updated? thank you for your help, I havent coded in a while and I did not know about what you were saying so thanks mypy 不警告实例化纯虚拟类 - mypy doesn't warn about instantiating pure virtual class 关于python /我的程序无法启动的2个问题 - 2 questions about python/ My program doesn't start
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM