4

注意: この質問は Python 2.7.3 を前提としています。

関数のローカル名前空間を動的に変更するための適切なアプローチを探しています。できれば、ボディ関数に最小限の混乱を追加する方法で。

私が考えていることは、次のようになります。

import os
from namespace_updater import update_locals

def somefunc(x, y, z):
    # ...
    # ...
    # this and that
    # ...
    # ...

    if os.environ.get('FROBNICATE'):
        from frobnitz import frobnicate
        update_locals(frobnicate(locals()))

    #
    # life goes on, possibly with duly frobnicated local variables...
    # ...
    # ...
    # ...

ありがとう!


PS: 以下は、機能しないアプローチです。

これに対する最も素朴なアプローチは、次のようなものです。

locals().update(new_locals(locals())

...しかし、ドキュメントではlocals()、そのようなブードゥーに依存してローカル変数を変更することに対して非常に明示的に警告しているため、これを回答として送信しないでください (ドキュメントの警告を無視する優れたケースを作成できない限り)。

ナイーブスケールの次は次のようなものです

for k, v in new_locals(locals()).items():
    exec ('%s = v' % k)

AFAICT、そのようなコードは「邪魔にならない」ことはできません(つまり、関数の本体にある必要があります)。これは理想的ではありません。しかし、本当の解決策は、exec ('%s = v' % k)ハッキングがいくつかの奇妙なバグにつながる可能性があるということです.

私が「奇妙なバグ」と書くとき、私が意味するのは「私のように理解力が乏しい人には奇妙に見えるバグexec ('%s = v' % k)」です。このハックについての私の理解はどれほど薄いですか? これに答えるには、以下のスクリプトを検討してください。これには 3 つのバリエーションがあります。(1) 示されているとおり。#(2) 18 行目の先頭を削除した後。(3) 15 行目と 18 行目の両方の最初の行を削除した後#(つまり、この亜種では、コードはコメントアウトされていません)。このスクリプトのバリアント (2) と (3) の動作は予測できませんでした。バリアント (1) の動作を 50% 以上の信頼度で予測することさえできませんでした。それは、私の理解がどれほど薄いかです。exec ('%s = v' % k)ハック。このスクリプトの 3 つの亜種が (python 2.7 の下で) どのように動作するかを自信を持って正しく予測できない限り、状況の把握は私のものと同じくらい希薄であると言っても過言ではありませexec ('%s = v' % k)ん。

x = 'global x'                            # 01
y = 'global y'                            # 02
def main():                               # 03
    x = 'local x'                         # 04
    y = 'local y'                         # 05
    run(locals())                         # 06
    print 'OK'                            # 07
    return 0                              # 08
                                          # 09
def run(namespace):                       # 10
    global y                              # 11
    print locals().keys()                 # 12
    for k, v in namespace.items():        # 13
        print '%s <- %r' % (k, v)         # 14
        exec ('%s = v' % k) #in locals()  # 15
    print locals().keys()                 # 16
    x = x                                 # 17
    #z = lambda: k                        # 18
    print x                               # 19
    print y                               # 20
                                          # 21
exit(main())                              # 22
4

3 に答える 3

1

私が考えることができる唯一の合理的なアプローチを提示し、それを使用しないように説得しようとします。

def process(**kw):
  mycode = """\
print 'Value of foo is %s' % (foo,)
print 'Value of bar is %s' % (bar,)
"""
  exec mycode in kw

vars = {'foo': 2, 'bar': 3}
process(**vars)

このアプローチにより、コード インジェクション攻撃から少なくともある程度保護されます。コードの「ローカル変数」を含むディクショナリが明示的に指定されるため、execステートメントを実行するときに変数スペースがどうなるかを完全に制御できます。関数オブジェクトなどの内部にハックする必要はありません。

動的に作成された関数の引数名を操作するためにデコレータ モジュールexecが の実装で使用されていることを知っています。それを使用する他の一般的なモジュールがあるかもしれません。しかし、私は、Python の代替案に明確に勝った@decorator1 つの状況と、 .execeval

あなたの質問にはそのような状況は見られません。mycodeで与えられた引数名を持つ関数を作成するなど、上から本当にファンクなことをする必要がない限り、コードを単純に書くだけで、おそらくピンチでkw使用するだけで済む可能性があります。locals()

def process(**kw):
  print 'Value of foo is %s' % (kw['foo'],)
  print 'Value of bar is %s' % (kw['bar'],)

process(foo=2, bar=3)
于 2012-05-07T21:20:14.603 に答える
0

そのようなものかもしれません

def foo():
    print(x)

foo.__globals__["x"] = "Hello Python"

foo()

残念ながら、変数が定義されている場合、これは関数の本体では機能しません

def foo(flag):
    x = "Hello World"
    if flag:
        foo.__globals__["x"] = "Hello Python"

    print(x)

両方のフラグでHello Worldを出力するのは True または False です

于 2012-05-07T20:19:09.420 に答える
0

外部関数のみで可能かどうかはわかりません。スニペットを作成しました:

def get_module_prefix(mod, localsDict):
    for name, value in localsDict.iteritems():
        if value == mod:
            return name
    raise Exception("Not found")

def get_new_locals(mod, localsDict):
    modulePrefix = get_module_prefix(mod, localsDict)
    stmts = []
    for name in dir(mod):
        if name.startswith('_'):
            continue
        if name not in localsDict:
            continue
        stmts.append("%s = %s.%s" % (name, modulePrefix, name))
    return "\n".join(stmts)

def func(someName):
    from some.dotted.prefix import some.dotted.name
    #here we update locals
    exec(get_new_locals(some.dotted.name, "some.dotted.name", locals()))
    print locals()
    print someName # value taken from aModule instead of parameter value


func(5)

どこ:

  • get_module_prefixモジュールがインポートされた名前を見つけるために使用されます。
  • get_new_localsローカルを更新するために使用できる割り当てステートメントを返します。

ローカルの実際の更新はexec(get_new_locals(some.dotted.name, locals()))、モジュールからローカル変数に値を代入する代入ステートメントを実行する行で実行されます。

それがあなたが実際に言っていることかどうかはわかりません。

于 2012-05-07T20:41:47.883 に答える