3

exec メソッドで使用する辞書をサブクラス化しようとしています。最終的には、ローカル関数にカスタムの名前スコープ動作を持たせたいと考えています。

以下のコードでは、関数b()は実際に利用可能な正しいglobals()辞書を持っていますが、解決時に検索に失敗しますz

関数はb()最初に検索しませんlocals()globals()?

非常に不可解です。どんな助けでも感謝します。

t = '''
def b():
#   return (globals()['z']) #works
    return z #fails

b()
'''

class MyDict(dict):
    def __init__(self, g):
        dict.__init__(self)
        self.my_g = g


    def __getitem__(self, key):
        print("GET ", key)
        try:
            val = dict.__getitem__(self, key)
        except:
            print("GET exception1")
            val = self.my_g[key]
        return val


g = {'z':123}

md = MyDict(g)

#fails to find z
exec(t, md, md)

#works
#exec(t, g, g)

出力

GET  b
Traceback (most recent call last):
  File "/project1/text12", line 31, in <module>
  File "<string>", line 6, in <module>
  File "<string>", line 4, in b
NameError: global name 'z' is not defined
4

1 に答える 1

5

Python 3.3より前では、execステートメントの値にカスタムサブクラスを使用することはできません。コンパイルされたコードを実行する基になるCコードは、実装したカスタムフックを無視して、基になるC構造体に直接アクセスします。dictglobals

つまり、コードがLOAD_GLOBAL操作を実行すると(z関数の場合のようにb)、Cバイトコード評価ループはC APIglobalsを使用して現在のフレームの構造にアクセスし、Pythonオーバーライドをバイパスします。

これは、exec()関数のドキュメントに次のように記載されています。

グローバルのみが提供されている場合は、グローバル変数とローカル変数の両方に使用されるディクショナリである必要があります。グローバル変数とローカル変数が指定されている場合、それらはそれぞれグローバル変数とローカル変数に使用されます。提供されている場合、ローカルは任意のマッピングオブジェクトにすることができます。

この制限はPython3.3で緩和されました。問題14385を参照してください。ドキュメントはまだ更新されていませんが、C APIアクセスにフォールバックする前に、カスタムマッピングをテストするようにバイトコード評価ループが更新されています。カスタムマッピングを使用する場合は、カスタムクラスPyObject_GetItemを呼び出す関数が使用されます。__getitem__

于 2012-08-29T19:39:52.210 に答える