临时变量赋值的Python上下文管理器

时间:2017-01-04 01:01:57

标签: python python-3.x with-statement contextmanager

我经常需要暂时将变量的值替换为其他内容,执行一些依赖于此变量的计算,然后将变量恢复为其原始值。 E.g:

var = 0
# Assign temporary value and do computation
var_ori = var
var = 1
do_something_with_var()  # Function that reads the module level var variable
# Reassign original value
var = var_ori

这似乎是使用上下文管理器(with语句)的明显机会。 Python标准库是否包含任何此类上下文管理器?

修改

我知道这种事情通常由其他更好的方法来处理,而不是临时更改变量。然而,我并没有要求明显的解决方法。

在我的实际工作案例中,我无法改变do_something_with_var功能。实际上这甚至不是一个函数,而是一串代码,它在全局命名空间的上下文中作为一些元编程的一部分进行评估。我给出的例子是我能想到的最简单的例子,它保留了我对临时变量的问题。我没有要求获得我的示例代码的解决方法(正确版本),而是要在我的书面问题上得到答案。

3 个答案:

答案 0 :(得分:4)

不,因为上下文管理员无法在调用者的范围内分配变量。 (任何认为您可以使用$ xcode-select --install $ sudo chown -R (whoami):admin /usr/local $ sudo xcode-select --reset $ softwareupdate --install $ sudo xcodebuild -license locals执行此操作的人,请尝试使用您在函数内部提出的上下文管理器。它无法工作。)

实用程序,可以使用不是局部变量的东西,例如模块全局变量,其他对象属性和dicts ...但是它们是{{3及其相关函数,因此在非测试环境中使用它们之前,应该强烈考虑其他替代方法。像"这样的操作暂时修改这个东西,然后恢复它"往往会导致令人困惑的代码,并可能表明你使用了太多的全局状态。

答案 1 :(得分:2)

您问题的简单答案:

  

Python标准库是否包含任何此类上下文管理器?

是"不,它没有。"

答案 2 :(得分:1)

我的错误,也许是这样的,它不是内置的:

class ContextTester(object):
    """Initialize context environment and replace variables when completed"""

    def __init__(self, locals_reference):
        self.prev_local_variables = locals_reference.copy()
        self.locals_reference = locals_reference

    def __enter__(self):
        pass

    def __exit__(self, exception_type, exception_value, traceback):
        self.locals_reference.update(self.prev_local_variables)



a = 5
def do_some_work():
    global a
    print(a)
    a = 8
print("Before context tester: {}".format(a))
with ContextTester(locals()) as context:
    print("In context tester before assignment: {}".format(a))
    a = 6
    do_some_work()
    print("In context tester after assignment: {}".format(a))
print("After context tester: {}".format(a))

输出:

Before context tester: 5
In context tester before assignment: 5
6
In context tester after assignment: 8
After context tester: 5

为清楚起见,您知道它实际上正在做某事:

class ContextTester(object):
    """Initialize context environment and replace variables when completed"""

    def __init__(self, locals_reference):
        self.prev_local_variables = locals_reference.copy()
        self.locals_reference = locals_reference

    def __enter__(self):
        pass

    def __exit__(self, exception_type, exception_value, traceback):
        #self.locals_reference.update(self.prev_local_variables)
        pass

a = 5
def do_some_work():
    global a
    print(a)
    a = 8
print("Before context tester: {}".format(a))
with ContextTester(locals()) as context:
    print("In context tester before assignment: {}".format(a))
    a = 6
    do_some_work()
    print("In context tester after assignment: {}".format(a))
print("After context tester: {}".format(a))
a = 5
print("Before context tester: {}".format(a))
with ContextTester(locals()) as context:
    print("In context tester before assignment: {}".format(a))
    a = 6
    print("In context tester after assignment: {}".format(a))
print("After context tester: {}".format(a))

输出:

Before context tester: 5
In context tester before assignment: 5
6
In context tester after assignment: 8
After context tester: 8

Before context tester: 5
In context tester before assignment: 5
In context tester after assignment: 6
After context tester: 6

你也可以这样做:

def wrapper_function(func, *args, **kwargs):
    prev_globals = globals().copy()
    func(*args, **kwargs)
    globals().update(prev_globals)

应该注意的是,如果你试图在一个函数中使用with语句,你会想要使用globals()作为对本地人的引用,它可能会产生意想不到的后果,反正可能仍然存在。

我根本不建议这样做,但应该有用。