ローカル辞書を変更する方法はありますか?
-
11-09-2019 - |
質問
locals
は、ローカル値の辞書を返す組み込み関数です。ドキュメントには次のように書かれています。
警告
この辞書の内容を変更しないでください。変更は、通訳者が使用するローカル変数の値に影響しない場合があります。
残念ながら、exec には Python 3.0 でも同じ問題があります。これを回避する方法はありますか?
使用事例
考慮する:
@depends("a", "b", "c", "d", "e", "f")
def test():
put_into_locals(test.dependencies)
depends は引数に指定された文字列をリストに格納します test.dependences
. 。これらの文字列は辞書のキーです d
. 。書けるようになりたいです put_into_locals
から値を引き出すことができるように d
そしてそれらを地元の人々に入れます。これは可能でしょうか?
解決
exec をテストしたところ、Python 2.6.2 で動作しました。
>>> def test():
... exec "a = 5"
... print a
...
>>> test()
5
Python 3.x を使用している場合、ローカル変数は実行時に辞書を使用せずに配列として最適化されるため、機能しなくなります。
Python が「exec ステートメント」を検出すると、Python はローカル ストレージを配列から辞書に強制的に切り替えます。ただし、「exec」は Python 3.x の関数であるため、ユーザーが「exec = 123」のようなことを行う可能性があるため、コンパイラーはこの区別を行うことができません。
http://bugs.python.org/issue4831
地元の人々にその場での関数を変更することは、いくつかの結果がなければ不可能です。通常、関数のローカルは辞書に保存されませんが、そのインデックスが既知のロケールからコンパイル時に決定される配列に保存されます。これは、少なくともexecによって追加された新しい地元の人々と衝突します。古いexectatementはこれを回避しました。なぜなら、コンパイラは、グローバル/地元の人の主張のない幹部が関数で発生した場合、その名前空間が「最適化されていない」、つまり、地元のアレイを使用していません。exec()は現在通常の関数であるため、コンパイラは「exec」が何に拘束されるかを知らないため、特別に扱うことはできません。
他のヒント
ローカル変数は代入ステートメントによって変更されます。
文字列の辞書キーがある場合は、それをローカル変数にしないでください。辞書キーとしてのみ使用してください。
もしあなたが絶対に しなければならない ローカル変数でこれを実行させます。
def aFunction( a, b, c, d, e, f ):
# use a, b, c, d, e and f as local variables
aFunction( **someDictWithKeys_a_b_c_d_e_f )
これにより、魔法のようなことを何もせずに、辞書からいくつかのローカル変数が取り込まれます。
これは不可能です。これは、後でパフォーマンスを最適化できるようにするためだと思います。Python バイトコードは、名前ではなくインデックスによってローカルを参照します。locals() が書き込み可能である必要がある場合、インタプリタによる最適化の実装が妨げられたり、最適化がより困難になったりする可能性があります。
このようにローカルを編集できることを保証するコア API は見つからないと確信しています。その API でそれができるなら、locals() にもこの制限がないからです。
すべてのローカルはコンパイル時に存在する必要があることを忘れないでください。コンパイル時にローカルにバインドされていない名前を参照すると、コンパイラはそれがグローバルであると想定します。コンパイル後にローカルを「作成」することはできません。
見る この質問 考えられる解決策の 1 つですが、これは重大なハッキングなので、実際にはやりたくないでしょう。
サンプル コードには基本的な問題があることに注意してください。
@depends("a", "b", "c", "d", "e", "f")
def test():
put_into_locals(test.dependencies)
"test.dependencies"
f が現在の関数である「f.dependency」を参照していません。実際のグローバル値「test」を参照しています。つまり、複数のデコレータを使用する場合は次のようになります。
@memoize
@depends("a", "b", "c", "d", "e", "f")
def test():
put_into_locals(test.dependencies)
「test」はmemoizeのラップされた関数であり、dependsの関数ではないため、機能しなくなります。パイソン 本当に 「現在実行中の関数」(およびクラス)を参照する方法が必要です。
それを変数に保存します。
refs = locals()
def set_pets():
global refs
animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
for i in range(len(animals)):
refs['pet_0%s' % i] = animals[i]
set_pets()
refs['pet_05']='bird'
print(pet_00, pet_02, pet_04, pet_01, pet_03, pet_05 )
>> dog fish monkey cat fox bird
そして、locals() に入れる前に dict をテストしたい場合は、次のようにします。
def set_pets():
global refs
sandbox = {}
animals = ('dog', 'cat', 'fish', 'fox', 'monkey')
for i in range(len(animals)):
sandbox['pet_0%s' % i] = animals[i]
# Test sandboxed dict here
refs.update( sandbox )
MacOS Sierra 上の Python 3.6.1
同じ制限が適用されるかどうかはわかりませんが、検査モジュールを通じて現在のフレーム (およびそこからローカル変数辞書) への直接参照を取得できます。
>>> import inspect
>>> inspect.currentframe().f_locals['foo'] = 'bar'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'foo', 'inspect']
>>> foo
'bar'