¿Es posible escribir en un objeto de marco de python tal como lo devuelve sys._getframe () desde el código de python que se ejecuta dentro del intérprete?

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

Pregunta

A propósito de Esto pregunta , hay un poco de andamiaje dentro del intérprete para inspeccionar los objetos de marco, que se pueden recuperar con sys._getframe () . Los objetos de marco parecen ser de solo lectura, pero no puedo encontrar nada obvio en los documentos que explícitamente indique esto. ¿Alguien puede confirmar si estos objetos son de escritura (de alguna manera) o de solo lectura?

import sys

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

if __name__ == '__main__':
    foobar()

Esto imprime ' foo ' cuando se ejecuta, pero la publicación a continuación muestra que la variable se puede escribir cuando se ejecuta desde el marco actual en un shell interactivo.

¿Fue útil?

Solución

Desde la fuente de 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 el PyMemberDef , las banderas RO o READONLY significa que sus atributos son de solo lectura. Para el PyGetSetDef , si solo tiene un captador, es de solo lectura. Esto significa que todos los atributos, pero f_exc_type , f_exc_value , f_exc_traceback y f_trace son de solo lectura después de la creación. Esto también se menciona en los documentos, en Modelo de datos .

Los objetos a los que se refieren los atributos no son necesariamente de solo lectura. Podrías hacer esto:

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

Aunque esto funciona en el intérprete, falla dentro de las funciones. El motor de ejecución utiliza una matriz separada para variables locales ( f_fastlocals ), que se fusiona en f_locals en el acceso, pero lo contrario no es cierto.

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

En el marco global, f_local se refiere a f_globals , lo que hace que este truco funcione en el intérprete. La modificación de f_globals funciona, pero afecta a todo el módulo.

Otros consejos

El ejemplo f_locals ['foo'] de NXC funciona porque el código está en el alcance del módulo. En ese caso, f_locals es f_globals y f_globals es modificable y las modificaciones se reflejan en el módulo.

Dentro del alcance de la función, los locales () y f_locals se pueden escribir, pero " [los cambios pueden no afectar los valores de las variables locales utilizadas por el intérprete]. " 1 Es una opción de implementación. En CPython hay un bytecode optimizado para variables locales, LOAD_FAST. En Python, las variables locales son (casi siempre) conocidas una vez que se define la función, y CPython usa una búsqueda de índice para obtener el valor de la variable, en lugar de una búsqueda de diccionario.

En teoría, la búsqueda en el diccionario podría representar esa tabla, pero eso es mucho trabajo por poca ganancia.

Las excepciones a " las variables locales son conocidas " son si la función usa una instrucción exec y el caso obsoleto de " from module import * " ;. El código de bytes generado es diferente y más lento para estos casos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top