Можно ли записать объект фрейма Python, возвращаемый sys._getframe () из кода Python, выполняемого в интерпретаторе?

StackOverflow https://stackoverflow.com/questions/626835

Вопрос

По поводу этой вопрос , в интерпретаторе есть небольшая подпорка для проверки объектов фрейма, которую можно получить с помощью 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 , если он имеет только геттер, он доступен только для чтения. Это означает, что все атрибуты, кроме 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 работает, но влияет на весь модуль.

Другие советы

Пример f_locals ['foo'] от NXC работает, потому что код находится в области видимости модуля. В этом случае f_locals - это f_globals, а f_globals - модифицируемый, и модификации отражаются в модуле.

Внутри области действия функции locals () и f_locals доступны для записи, но " [изменения могут не влиять на значения локальных переменных, используемых интерпретатором]. " 1 Это вариант реализации. В CPython есть оптимизированный байт-код для локальных переменных, LOAD_FAST. В Python локальные переменные (почти всегда) известны после определения функции, и CPython использует поиск по индексу для получения значения переменной, а не поиск по словарю.

Теоретически поиск по словарю может проксировать эту таблицу, но это большая работа для небольшого выигрыша.

Исключения для " локальных переменных известны " если функция использует оператор exec и устаревший регистр «из модуля import *». Сгенерированный байт-код отличается и медленнее для этих случаев.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top