È possibile scrivere su un oggetto frame Python come restituito da sys._getframe () dal codice Python in esecuzione all'interno dell'interprete?
-
06-07-2019 - |
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.
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.