インタープリター内で実行されているpythonコードからsys._getframe()によって返されたpythonフレームオブジェクトに書き込むことは可能ですか?
-
06-07-2019 - |
質問
これの適切な質問では、インタープリタ内にフレームオブジェクトを検査するための足場が少しあります。これは 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 *"の非推奨のケースです。これらの場合、生成されるバイトコードは異なり、低速です。