3

たとえば、コードを実行したいとします。

    value += 5

私自身の名前空間内(したがって、結果は本質的に ですmydict['value'] += 5)。function がありexec()ますが、そこに文字列を渡す必要があります:

    exec('value += 5', mydict) 

ステートメントを文字列として渡すのは奇妙に思えます (たとえば、そのように色付けされていません)。次のようにできますか?

    def block():
        value += 5

    ???(block, mydict)

? 最後の行の明らかな候補は でしexec(block.__code__, mydict)たが、運が悪かった:UnboundLocalErrorvalue. block 内のコードblock()ではなく、基本的に を実行すると思います。そのため、割り当ては簡単ではありません。正しいですか?

もちろん、別の可能な解決策は、分解することblock.__code__です...

参考までに、このスレッドのために質問を受けました。また、これが一部の(私は未定)が新しい構文を要求する理由です

    using mydict: 
        value += 5

これがどのようにエラーをスローしないmydictか、どちらも変更しないことに注意してください。

    def block(value = 0):
        value += 5

    block(**mydict)
4

4 に答える 4

7

文字列の代わりにバイトコードを に渡すことができexecます。目的に応じて適切なバイトコードを作成する必要があります。

>>> bytecode = compile('value += 5', '<string>', 'exec')
>>> mydict = {'value': 23}
>>> exec(bytecode, mydict)
>>> mydict['value']
28

具体的には、 ...:

>>> import dis
>>> dis.dis(bytecode)
  1           0 LOAD_NAME                0 (value)
              3 LOAD_CONST               0 (5)
              6 INPLACE_ADD         
              7 STORE_NAME               0 (value)
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE        

ロード命令とストア命令は _NAME の説得力を持つ必要があり、これcompileによりそうなりますが、...:

>>> def f(): value += 5
... 
>>> dis.dis(f.func_code)
  1           0 LOAD_FAST                0 (value)
              3 LOAD_CONST               1 (5)
              6 INPLACE_ADD         
              7 STORE_FAST               0 (value)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        

...関数内のコードは _FAST バージョンを使用するように最適化されており、それらは に渡された dict では機能しませんexec。_FAST 命令を使用して何らかの方法でバイトコードを開始した場合は、代わりに _NAME 種類を使用するようにパッチを当てることができます。たとえば、バイトコードハックまたは同様のアプローチを使用します。

于 2009-08-14T22:25:28.063 に答える
3

これは、「カスタムローカル」を使用するようなブロックを作成するためのクレイジーなデコレータです。実際には、関数内のすべての変数アクセスをグローバルアクセスに変換し、環境としてカスタムローカルディクショナリを使用して結果を評価するのは簡単なハックです。

import dis
import functools
import types
import string

def withlocals(func):
    """Decorator for executing a block with custom "local" variables.

    The decorated function takes one argument: its scope dictionary.

    >>> @withlocals
    ... def block():
    ...     counter += 1
    ...     luckynumber = 88

    >>> d = {"counter": 1}
    >>> block(d)
    >>> d["counter"]
    2
    >>> d["luckynumber"]
    88
    """
    def opstr(*opnames):
        return "".join([chr(dis.opmap[N]) for N in opnames])

    translation_table = string.maketrans(
            opstr("LOAD_FAST", "STORE_FAST"),
            opstr("LOAD_GLOBAL", "STORE_GLOBAL"))

    c = func.func_code
    newcode = types.CodeType(c.co_argcount,
                             0, # co_nlocals
                             c.co_stacksize,
                             c.co_flags,
                             c.co_code.translate(translation_table),
                             c.co_consts,
                             c.co_varnames, # co_names, name of global vars
                             (), # co_varnames
                             c.co_filename,
                             c.co_name,
                             c.co_firstlineno,
                             c.co_lnotab)

    @functools.wraps(func)
    def wrapper(mylocals):
        return eval(newcode, mylocals)
    return wrapper

if __name__ == '__main__':
    import doctest
    doctest.testmod()

これは、 gotoデコレータのための誰かの素晴らしいレシピのモンキーパッチの適応です

于 2009-12-04T04:52:37.740 に答える
0

上記の S.Lott のコメントから、新しいクラスの作成を使用して答えを得ることができると思います。

class _(__metaclass__ = change(mydict)):
    value += 1
    ...

whereは、辞書を読み取り、辞書を更新changeするメタクラスです。__prepare____new__

再利用する場合は、以下のスニペットが機能しますが、見栄えが悪いです。

def increase_value(d):
    class _(__metaclass__ = change(d)):
        value += 1
        ...

increase_value(mydict)
于 2009-08-21T22:54:06.483 に答える