Est-il possible d'écrire dans un objet cadre Python tel que renvoyé par sys._getframe () à partir du code python exécuté dans l'interpréteur?

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

Question

A propos de Cette Pour la question , l'interpréteur contient un peu d'échafaudage pour inspecter les objets cadre, qui peut être récupéré par sys._getframe () . Les objets frame semblent être en lecture seule, mais je ne trouve rien d’évident dans la documentation qui le stipule explicitement. Quelqu'un peut-il confirmer si ces objets sont en écriture (en quelque sorte) ou en lecture seule?

import sys

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

if __name__ == '__main__':
    foobar()

Ceci affiche ' toto ' lors de l'exécution, mais l'article ci-dessous montre que la variable est accessible en écriture lorsqu'elle est exécutée à partir de l'image actuelle dans un shell interactif.

Était-ce utile?

La solution

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

Pour PyMemberDef , les indicateurs RO ou READONLY signifient que ses attributs sont en lecture seule. Pour PyGetSetDef , s'il ne dispose que d'un getter, il est en lecture seule. Cela signifie que tous les attributs sauf type f_exc , valeur f_exc , f_exc_traceback et f_trace sont en lecture seule après leur création. Ceci est également mentionné dans la documentation, sous le Modèle de données .

Les objets référencés par les attributs ne sont pas nécessairement en lecture seule. Vous pouvez faire ceci:

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

Bien que cela fonctionne dans l'interpréteur, il échoue dans les fonctions. Le moteur d'exécution utilise un tableau distinct pour les variables locales ( f_fastlocals ), qui est fusionné dans f_locals lors de l'accès, mais l'inverse n'est pas vrai.

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

Dans le cadre global, f_local fait référence à f_globals , ce qui permet à cette astuce de fonctionner dans l'interpréteur. La modification de f_globals fonctionne, mais affecte tout le module.

Autres conseils

L'exemple f_locals ['foo'] de NXC fonctionne car le code est dans la portée du module. Dans ce cas, f_locals est f_globals et f_globals est modifiable et les modifications sont reflétées dans le module.

À l'intérieur de l'étendue de la fonction, locals () et f_locals sont accessibles en écriture, mais les modifications "[peuvent ne pas affecter les valeurs des variables locales utilisées par l'interpréteur]". 1 C'est un choix d'implémentation. Dans CPython, il existe un bytecode optimisé pour les variables locales, LOAD_FAST. En Python, les variables locales sont (presque toujours) connues une fois la fonction définie et CPython utilise une recherche d’index pour obtenir la valeur de la variable, plutôt qu’une recherche dans un dictionnaire.

En théorie, la recherche dans le dictionnaire pourrait remplacer cette table par un proxy, mais cela demande beaucoup de travail pour un gain minime.

Les exceptions aux " variables locales sont connues " are si la fonction utilise une instruction exec et le cas déconseillé de "from module import *". Le code d'octet généré est différent et plus lent pour ces cas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top