2

关于这个问题,解释器中有一些脚手架来检查框架对象,可以通过sys._getframe(). 框架对象似乎是只读的,但我在明确说明这一点的文档中找不到任何明显的东西。有人可以确认这些对象是可写的(以某种方式)还是只读的?

import sys

def foobar():
    xx='foo'
    ff = sys._getframe()
    ff.f_locals['xx'] = 'bar'
    print xx

if __name__ == '__main__':
    foobar()

这会在运行时打印出 ' foo',但下面的帖子演示了在交互式 shell 中从当前帧运行时该变量是可写的。

4

2 回答 2

12

来自 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,标志ROREADONLY意味着它的属性是只读的。对于PyGetSetDef,如果它只有一个吸气剂,它是只读的。这意味着除了 、 和 之外的所有属性f_exc_type在创建f_exc_value后都是只读的。这也在文档中的Data model下提到。f_exc_tracebackf_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有效,但会影响整个模块。

于 2009-03-09T16:15:24.097 回答
1

NXC 的 f_locals['foo'] 示例有效,因为代码在模块范围内。在这种情况下,f_locals 是 f_globals,而 f_globals 是可修改的,修改会反映在模块中。

在函数范围内,locals() 和 f_locals 是可写的,但是“[更改可能不会影响解释器使用的局部变量的值]。” 1这是一种实施选择。在 CPython 中,有一个针对局部变量的优化字节码 LOAD_FAST。在 Python 中,一旦定义了函数,局部变量(几乎总是)是已知的,并且 CPython 使用索引查找来获取变量值,而不是字典查找。

从理论上讲,字典查找可以代理该表,但这是一项收效甚微的工作。

“已知局部变量”的例外情况是函数使用 exec 语句,以及不推荐使用的“from module import *”。对于这些情况,生成的字节码是不同的,而且速度较慢。

于 2009-03-09T17:08:37.307 回答