É possível gravar em um quadro de objeto python como retornado por sys._getframe () a partir do código python em execução dentro do intérprete?

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

Pergunta

A propósito de Este questão, há um pouco de andaimes no intérprete para inspeccionar objectos quadro, que podem ser recuperados por sys._getframe(). Os objetos de quadro só aparecem para ser lido, mas eu não consigo encontrar nada óbvio na documentação que afirma explicitamente isso. Alguém pode confirmar se esses objetos são gravável (de alguma forma) ou somente leitura?

import sys

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

if __name__ == '__main__':
    foobar()

Isso mostra 'foo' quando executado, mas o post abaixo demonstra a variável sendo gravável quando executado a partir do quadro atual em um shell interativo.

Foi útil?

Solução

De fonte 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}
};

Para o PyMemberDef, as bandeiras RO ou meios READONLY seus atributos são somente leitura. Para o PyGetSetDef, se ele só tem um getter, é só ler. Isto significa que todos atributos, mas f_exc_type, f_exc_value, f_exc_traceback e f_trace são somente leitura após a criação. Este é também mencionado nos documentos, sob dados modelo .

Os objectos referidos pelos atributos não é necessariamente só de leitura. Você poderia fazer isso:

>>> f = sys._getframe()
>>> f.f_locals['foo'] = 3
>>> foo
3
>>>

Embora isso funciona da intérprete, ele falhar funções dentro. O mecanismo de execução usa uma matriz separada para as variáveis ??locais (f_fastlocals), que é mesclada f_locals no acesso, mas o inverso não é verdadeiro.

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

No quadro global, f_local refere-se a f_globals, o que torna este trabalho truque no intérprete. Modificando obras f_globals, mas afeta todo o módulo.

Outras dicas

Os f_locals [ 'foo'] exemplo, NXC funciona porque o código está no escopo do módulo. Nesse caso, é f_locals f_globals, e f_globals é tanto modificável e modificações reflectem-se no módulo.

Dentro do escopo da função, os moradores () e f_locals são graváveis, mas "[mudanças podem não afetar os valores das variáveis ??locais utilizados pelo intérprete]." 1 É uma escolha de implementação. Em CPython há um bytecode otimizado para variáveis ??locais, LOAD_FAST. Em Python, variáveis ??locais são (quase sempre) conhecido uma vez que a função é definida, e CPython usa uma consulta de índice para obter o valor da variável, ao invés de uma pesquisa no dicionário.

Em teoria, a pesquisa de dicionário poderia proxy que mesa, mas isso é um monte de trabalho para pouco ganho.

As exceções a "variáveis ??locais são conhecidos" são se a função usa uma instrução exec, eo caso reprovado de "do módulo de importação *". O código byte gerado é diferente, e mais lento, para estes casos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top