È possibile scrivere su un oggetto frame Python come restituito da sys._getframe () dal codice Python in esecuzione all'interno dell'interprete?

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

Domanda

A proposito di Questo domanda , c'è un po 'di impalcature all'interno dell'interprete per ispezionare gli oggetti frame, che possono essere recuperati da sys._getframe () . Gli oggetti frame sembrano essere di sola lettura, ma non riesco a trovare nulla di ovvio nei documenti che lo afferma esplicitamente. Qualcuno può confermare se questi oggetti sono scrivibili (in qualche modo) o di sola lettura?

import sys

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

if __name__ == '__main__':
    foobar()

Questo stampa ' foo ' quando viene eseguito ma il post seguente mostra che la variabile è scrivibile quando viene eseguita dal frame corrente in una shell interattiva.

È stato utile?

Soluzione

Dalla 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}
};

Per PyMemberDef , i flag RO o READONLY indicano che i suoi attributi sono di sola lettura. Per il PyGetSetDef , se ha solo un getter, è di sola lettura. Ciò significa che tutti gli attributi tranne f_exc_type , f_exc_value , f_exc_traceback e f_trace sono di sola lettura dopo la creazione. Questo è anche menzionato nei documenti, in Modello dati .

Gli oggetti a cui fanno riferimento gli attributi non sono necessariamente di sola lettura. Puoi farlo:

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

Sebbene funzioni nell'interprete, non funziona all'interno delle funzioni. Il motore di esecuzione utilizza un array separato per le variabili locali ( f_fastlocals ), che viene unito in f_locals all'accesso, ma il contrario non è vero.

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

Nel frame globale, f_local si riferisce a f_globals , che fa funzionare questo trucco nell'interprete. La modifica di f_globals funziona, ma influisce sull'intero modulo.

Altri suggerimenti

L'esempio f_locals ['foo'] di NXC funziona perché il codice è nell'ambito del modulo. In tal caso, f_locals è f_globals e f_globals è modificabile e le modifiche si riflettono nel modulo.

All'interno dell'ambito della funzione, locals () e f_locals sono scrivibili, ma "[le modifiche potrebbero non influire sui valori delle variabili locali utilizzate dall'interprete]. " 1 È una scelta di implementazione. In CPython esiste un bytecode ottimizzato per le variabili locali, LOAD_FAST. In Python, le variabili locali sono (quasi sempre) conosciute una volta definita la funzione e CPython utilizza una ricerca indice per ottenere il valore della variabile, piuttosto che una ricerca nel dizionario.

In teoria la ricerca del dizionario potrebbe delegare quella tabella, ma è molto lavoro per poco guadagno.

Le eccezioni a " variabili locali sono note " sono se la funzione utilizza un'istruzione exec e il caso deprecato di " from module import * " ;. Il codice byte generato è diverso e più lento, in questi casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top