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

Python context manager for temporary variable assignment

我经常需要将一个变量的值临时换成其他东西,做一些依赖于这个变量的计算,然后将变量恢复到它的原始值。例如:

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

不,因为上下文管理器不能像那样在调用者的范围内分配变量。 (任何认为您可以使用 localsinspect 做到这一点的人,请尝试使用您在函数内部提出的上下文管理器。它不会起作用。)

实用程序可以处理非局部变量的事情,例如模块全局变量、其他对象属性和字典...但它们是 unittest.mock.patch 及其相关函数,因此在非测试上下文中使用它们之前,您应该认真考虑其他替代方案。 "temporarily modify this thing and then restore it" 之类的操作往往会导致代码混乱,并且可能表明您使用了过多的全局状态。

您问题的简单答案:

Does the Python standard library contain any such context manager?

是"No, it does not."

我的错误,而不是像这样的东西,它不是内置的:

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() 作为对局部变量的引用,这可能会产生意想不到的后果,无论如何可能仍然如此。

我根本不建议这样做,但应该可行。