インタープリター内で実行されているpythonコードからsys._getframe()によって返されたpythonフレームオブジェクトに書き込むことは可能ですか?

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

質問

これの適切な質問では、インタープリタ内にフレームオブジェクトを検査するための足場が少しあります。これは sys._getframe()によって取得できます。フレームオブジェクトは読み取り専用のように見えますが、これを明示的に述べているドキュメントには明らかなものは見つかりません。これらのオブジェクトが(何らかの方法で)書き込み可能か読み取り専用かを誰かが確認できますか?

import sys

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

if __name__ == '__main__':
    foobar()

これは実行時に ' foo 'を出力しますが、以下の投稿では、対話型シェルの現在のフレームから実行したときに書き込み可能な変数を示しています。

役に立ちましたか?

解決

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

PyMemberDef の場合、フラグ RO または READONLY は、属性が読み取り専用であることを意味します。 PyGetSetDef の場合、ゲッターのみがある場合、読み取り専用です。これは、 f_exc_type f_exc_value f_exc_traceback 、および f_trace を除くすべての属性が作成後に読み取り専用であることを意味します。これは、ドキュメントのデータモデルにも記載されています。

>

属性によって参照されるオブジェクトは、必ずしも読み取り専用ではありません。これを行うことができます:

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

これはインタープリターでは機能しますが、関数内では失敗します。実行エンジンはローカル変数( f_fastlocals )に別の配列を使用します。これはアクセス時に f_locals にマージされますが、逆は成り立ちません。

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

グローバルフレームでは、 f_local f_globals を参照します。これにより、このトリックがインタープリターで機能します。 f_globals の変更は機能しますが、モジュール全体に影響します。

他のヒント

NXCによるf_locals ['foo']の例は、コードがモジュールスコープ内にあるため機能します。その場合、f_localsはf_globalsであり、f_globalsは変更可能であり、変更はモジュールに反映されます。

関数スコープの内部では、locals()およびf_localsは書き込み可能ですが、「変更はインタープリターが使用するローカル変数の値に影響を与えない可能性があります」。 1 これは実装の選択です。 CPythonには、ローカル変数の最適化されたバイトコードLOAD_FASTがあります。 Pythonでは、関数が定義されるとローカル変数が(ほぼ常に)認識され、CPythonは辞書ルックアップではなくインデックスルックアップを使用して変数値を取得します。

理論的には、辞書検索はそのテーブルをプロキシすることができますが、それは少しの利益のために多くの作業です。

「ローカル変数は既知」の例外関数がexecステートメントを使用する場合、および" from module import *"の非推奨のケースです。これらの場合、生成されるバイトコードは異なり、低速です。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top