5

withステートメント(リンク)の仕様を読んで、私がいじってみたいことがいくつかあります。これは本番コードなどには当てはまりません。私は調査中ですので、これが悪い考えである場合は、厳しすぎないでください。

私がやりたいのは、上記のリンクされたドキュメントで「BLOCK」と呼ばれる部分を取得し、実際にへの呼び出しの中でそれをいじくり回すこと__enter__です。(動機と要約のセクションの開始直後に、リンクされたドキュメントを参照してください。)

アイデアは、私自身の種類のオンザフライローカル名前空間を作成することです。このようなもの:

with MyNameSpace(some_object):
    print a #Should print some_object.a
    x = 4 #Should set some_object.x=4

基本的に、ブロック内のステートメントはwith、のローカル変数と代入規則に従属するようにしますsome_object

私の特定のケースでsome_objectは、私自身の列単位の操作などを持つ特別なデータ配列である可能性があります。その場合、のようなことを言うことx = y + 5 if y > 4 else y - 2は、内部でいくつかの派手なNumPyベクトル化された操作かもしれませんが、some_objectそれらのメソッドへのインターフェースを明示的に呼び出す必要はありません。名前空間では、式は「正しく機能する」必要があります(ただし、MyNameSpaceクラスで推測されるように定義します。

私の最初のアイデアは、どういうわけかプロセスを中断し、ブロックwithに入るコードを取得することです。try次に、呼び出されたときにそのコードを解釈し__enter__、ブロック内のコードを別のコードに置き換えtryます(おそらくそれが機能する場合でも、新しい変更された変数を保持したまま元の変数スコープにpass復元するもの)。some_object

単純なテストケースは次のようになります。

my_dict = {'a':3, 'b':2}
with MyNameSpace(my_dict):
    print a # Should print 3
    x = 5 # When the block finishes, my_dict['x'] should now be 5

このアイデアがすでにどこかに存在するかどうか興味があります。

変数を割り当てるためのベストプラクティスを知っています。これはペットプロジェクトであるため、このアイデアのために、ベストプラクティスを無視できると想定してください。この方法で変数を割り当てたくない場合でも、現在のプロジェクトで役立つ可能性があります。

編集

私がやりたいと思うかもしれないトリッキーなことの種類を明確にし、それができないと主張する以下の答えに対処するために、testLocals.py以下のサンプルファイルを検討してください。

my_dict = {'a':1, 'b':2}
m = locals()
print m["my_dict"]['a']
m["my_dict"]['c'] = 3
print my_dict

class some_other_scope(object):
    def __init__(self, some_scope):
        x = 5
        g = locals()
        some_scope.update(g)
        some_scope["my_dict"]["d"] = 4

sos = some_other_scope(m)
print my_dict
print x

非対話的に実行すると、次のようになります。

ely@AMDESK:~/Desktop/Programming/Python$ python testLocals.py
1
{'a': 1, 'c': 3, 'b': 2}
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
5
4

2 に答える 2

5

これを試して。

import sys 
class MyNameSpace(object):
    def __init__(self,ns):
        self.ns = ns
    def __enter__(self):
        globals().update(self.ns)
    def __exit__(self, exc_type,exc_value,traceback):
        self.ns.update(sys._getframe(1).f_locals)

my_dict = {'a':3, 'b':2} 
with MyNameSpace(my_dict) as ns:
    print(a) # Should print 3
    x = 5 # When the block finishes, my_dict['x'] should now be 5 

print(my_dict['x'])
于 2012-09-18T22:36:47.847 に答える
2

これは私が数ヶ月前に試したのと似たものです:

import sys
import inspect
import collections

iscallable = lambda x: isinstance(x, collections.Callable)

class Namespace(object):
    def __enter__(self):
        """store the pre-contextmanager scope"""
        f = inspect.currentframe(1)
        self.scope_before = dict(f.f_locals)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """determine the locally declared objects"""
        f = inspect.currentframe(1)
        scope_after = dict(f.f_locals)
        scope_context = set(scope_after) - set(self.scope_before)

        # capture the local scope, ignoring the context manager itself
        self.locals = dict(
            (k, scope_after[k]) for k in scope_context if not isinstance(scope_after[k], self.__class__)
        )

        for name in self.locals:
            obj = scope_after[name]
            if iscallable(obj):
                # closure around the func_code with the appropriate locals
                _wrapper = type(lambda: 0)(obj.func_code, self.locals)
                self.__dict__[name] = _wrapper
                # update locals so the calling functions refer to the wrappers too
                self.locals[name] = _wrapper
            else:
                self.__dict__[name] = obj

            # remove from module scope
            del sys.modules[__name__].__dict__[name]

        return self

with Namespace() as Spam:
    x = 1
    def ham(a):
        return x + a      
    def cheese(a):
        return ham(a) * 10

inspectを使用して、コンテキストマネージャー内でローカルを変更し、完了したら元の値に再割り当てします。

それは完璧ではありません-どこで問題が発生したかは思い出せませんが、確かにそうです-しかし、それはあなたが始めるのに役立つかもしれません。

于 2012-09-18T22:27:50.690 に答える