问题描述
问题,解释器内部有一些脚手架来检查帧对象,可以通过sys._getframe()检索.框架对象似乎仅读取,但是在文档中我找不到明显说明这一点的任何明显的东西.有人可以确认这些对象是否可以写入(某种方式)或仅读取?
import sys def foobar(): xx='foo' ff = sys._getframe() ff.f_locals['xx'] = 'bar' print xx if __name__ == '__main__': foobar()
运行时此打印出'foo',但下面的帖子演示了从当前帧中运行交互式外壳的变量.
推荐答案
来自cpython源,Objects/frameobject.c:
static PyMemberDef frame_memberlist[] = { {"f_back", T_OBJECT, OFF(f_back), RO}, {"f_code", T_OBJECT, OFF(f_code), RO}, {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, {"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, {NULL} /* Sentinel */ }; ... static PyGetSetDef frame_getsetlist[] = { {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, {"f_restricted",(getter)frame_getrestricted,NULL, NULL}, {0} };
对于PyMemberDef,标志RO或READONLY表示它的属性仅是读取的.对于PyGetSetDef,如果它只有一个getter,则仅读取.这意味着所有属性,但是f_exc_type,f_exc_value,f_exc_traceback和f_trace在创建后仅读取.文档中也提到了这一点,在数据模型.
属性所指的对象不一定是只读的.您可以这样做:
>>> f = sys._getframe() >>> f.f_locals['foo'] = 3 >>> foo 3 >>>
尽管这在解释器中起作用,但它在功能中失败.执行引擎对本地变量(f_fastlocals)使用单独的数组,该数组在访问时合并到f_locals中,但是相反的不正确.
>>> def foo(): ... x = 3 ... f = sys._getframe() ... print f.f_locals['x'] ... x = 4 ... print f.f_locals['x'] ... d = f.f_locals ... x = 5 ... print d['x'] ... f.f_locals ... print d['x'] ... >>> foo() 3 4 4 5 >>>
在全局框架上,f_local是指f_globals,这使该技巧在解释器中起作用.修改f_globals有效,但会影响整个模块.
其他推荐答案
NXC的f_locals ['foo']示例工作,因为代码在模块范围中.在这种情况下,f_locals是f_globals,f_globals既可以修改,又反映在模块中.
在功能范围内,本地()和f_locals内部的内部是可写的,但" [变化可能不会影响解释器使用的局部变量的值]". 1 这是一个实现选择.在CPYTHON中,有一个针对本地变量的优化字节码,load_fast.在Python中,一旦定义了该函数,局部变量(几乎总是)已知,并且CPYTHON使用索引查找来获取变量值,而不是字典查找.
从理论上讲,字典查找可以代理该桌子,但这是很少的收益工作.
如果该函数使用EXEC语句,则"局部变量已知"的例外是"来自模块导入 *"的弃用情况.对于这些情况,生成的字节代码不同,较慢.
问题描述
Apropos of This question, there is a bit of scaffolding within the interpreter to inspect frame objects, which can be retrieved by sys._getframe(). The frame objects appear to be read only, but I can't find anything obvious in the docs that explicitly states this. Can someone confirm whether these objects are writeable (in some way) or read only?
import sys def foobar(): xx='foo' ff = sys._getframe() ff.f_locals['xx'] = 'bar' print xx if __name__ == '__main__': foobar()
This prints out 'foo' when run but the post below demonstrates the variable being writable when run from the current frame in an interactive shell.
推荐答案
From CPython source, Objects/frameobject.c:
static PyMemberDef frame_memberlist[] = { {"f_back", T_OBJECT, OFF(f_back), RO}, {"f_code", T_OBJECT, OFF(f_code), RO}, {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, {"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, {NULL} /* Sentinel */ }; ... static PyGetSetDef frame_getsetlist[] = { {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, (setter)frame_setlineno, NULL}, {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, {"f_restricted",(getter)frame_getrestricted,NULL, NULL}, {0} };
For the PyMemberDef, the flags RO or READONLY means it's attributes are read-only. For the PyGetSetDef, if it only has a getter, it's read only. This means all attributes but f_exc_type, f_exc_value, f_exc_traceback and f_trace are read-only after creation. This is also mentioned in the docs, under Data model.
The objects referred to by the attributes is not necessarily read-only. You could do this:
>>> f = sys._getframe() >>> f.f_locals['foo'] = 3 >>> foo 3 >>>
Though this works in the interpreter, it fails inside functions. The execution engine uses a separate array for local variables (f_fastlocals), which is merged into f_locals on access, but the converse is not true.
>>> def foo(): ... x = 3 ... f = sys._getframe() ... print f.f_locals['x'] ... x = 4 ... print f.f_locals['x'] ... d = f.f_locals ... x = 5 ... print d['x'] ... f.f_locals ... print d['x'] ... >>> foo() 3 4 4 5 >>>
On the global frame, f_local refers to f_globals, which makes this trick work in the interpreter. Modifying f_globals works, but affects the whole module.
其他推荐答案
The f_locals['foo'] example by NXC works because the code is in module scope. In that case, f_locals is f_globals, and f_globals is both modifiable and modifications are reflected in the module.
Inside of function scope, locals() and f_locals are writable, but "[changes may not affect the values of local variables used by the interpreter]." 1 It's an implementation choice. In CPython there's a optimized bytecode for local variables, LOAD_FAST. In Python, local variables are (almost always) known once the function is defined, and CPython uses an index lookup to get the variable value, rather than a dictionary lookup.
In theory the dictionary lookup could proxy that table, but that's a lot of work for little gain.
The exceptions to "local variables are known" are if the function uses an exec statement, and the deprecated case of "from module import *". The generated byte code is different, and slower, for these cases.