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__替换块中的代码(也许如果这样可行,但可能会恢复到原始变量范围并保留其新更改的变量)。trypasssome_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

它在上下文管理器中使用检查来修改局部变量,然后在完成后重新分配回原始值。

它并不完美——我不记得它在哪里遇到了问题,但我确信它确实如此——但它可能会帮助你入门。

于 2012-09-18T22:27:50.690 回答