我在这个问题上找到了一些模糊的相关问题,但没有为CPython找到任何干净而具体的解决方案。我认为“有效”的解决方案是特定的解释器。
首先我认为我理解的事情:
locals()
提供了一个不可修改的词典。frame.f_locals
提供locals()
字典,但不太容易通过exec
来破解事物。或者至少我没有能力做像locals()['var'] = value ; exec ""
exec
能够对局部变量做奇怪的事情,但它不可靠 - 例如。我在某处读到它在Python 3中不起作用。还没有测试过。所以我理解,鉴于这些限制,将额外变量添加到本地变量永远不会安全,因为它会破坏解释器结构。
但是,应该可以更改已存在的变量,不是吗?
我考虑的事情
f
中,可以访问f.func_code.co_nlocals
和f.func_code.co_varnames
。frame.f_locals
访问/检查/读取变量。这是通过sys.settrace
设置跟踪器的用例。变量应该在某个地方,最好是可写的...但我无法找到它。即使它是一个数组(用于解释器高效访问),或者我需要一些额外的C特定接线,我已准备好承诺它。
如何从跟踪器函数或装饰包装函数或类似函数中实现对变量的修改?
一个完整的解决方案当然会受到赞赏,但即使是一些指针也会对我有很大帮助,因为我被困在这里有很多不可写的词典: - /
等操作答案 0 :(得分:4)
它存在一个未记录的C-API调用,用于执行这样的操作:
PyFrame_LocalsToFast
this PyDev blog post还有一些讨论。基本想法似乎是:
import ctypes
...
frame.f_locals.update({
'a': 'newvalue',
'b': other_local_value,
})
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame), ctypes.c_int(0))
我还没有测试它是否按预期工作。
请注意,可能有某种方法可以直接访问Fast
,以避免在需求仅修改现有变量时出现间接。但是,由于这似乎主要是非文档化的API,源代码是文档资源。
答案 1 :(得分:2)
根据MariusSiuram的笔记,我写了一个显示行为的食谱。
结论是:
所以,这是代码:
import inspect
import ctypes
def parent():
a = 1
z = 'foo'
print('- Trying to add a new variable ---------------')
hack(case=0) # just try to add a new variable 'b'
print(a)
print(z)
assert a == 1
assert z == 'foo'
try:
print (b)
assert False # never is going to reach this point
except NameError, why:
print("ok, global name 'b' is not defined")
print('- Trying to remove an existing variable ------')
hack(case=1)
print(a)
assert a == 2
try:
print (z)
except NameError, why:
print("ok, we've removed the 'z' var")
print('- Trying to update an existing variable ------')
hack(case=2)
print(a)
assert a == 3
def hack(case=0):
frame = inspect.stack()[1][0]
if case == 0:
frame.f_locals['b'] = "don't work"
elif case == 1:
frame.f_locals.pop('z')
frame.f_locals['a'] += 1
else:
frame.f_locals['a'] += 1
# passing c_int(1) will remove and update variables as well
# passing c_int(0) will only update
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(frame),
ctypes.c_int(1))
if __name__ == '__main__':
parent()
输出如下:
- Trying to add a new variable ---------------
1
foo
ok, global name 'b' is not defined
- Trying to remove an existing variable ------
2
foo
- Trying to update an existing variable ------
3