简体   繁体   English

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

[英]Python context manager for temporary variable assignment

I'm often in the need of temporarily switching the value of a variable out with something else, do some computation which depends on this variable, and then restore the variable to its original value.我经常需要用别的东西临时切换一个变量的值,做一些依赖于这个变量的计算,然后将变量恢复到它的原始值。 Eg:例如:

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

This seems like an obvious opportunity for using a context manager (the with statement).这似乎是使用上下文管理器( with语句)的明显机会。 Does the Python standard library contain any such context manager? Python 标准库是否包含任何这样的上下文管理器?

Edit编辑

I am aware that this sort of thing is most often handled by other, better means than temporarily changing a variable.我知道这种事情通常由其他更好的方法处理,而不是临时更改变量。 I am however not asking for obvious workarounds.然而,我并不是要求明显的解决方法。

In my actual work case, I can not alter the do_something_with_var function.在我的实际工作案例中,我无法更改do_something_with_var函数。 Actually this is not even a function, but a string of code which gets evaluated in the context of the global namespace as part of some metaprogramming.实际上,这甚至不是一个函数,而是作为元编程的一部分在全局命名空间上下文中求值的一串代码。 The example I gave was the simplest I could think of that kept my problem with the temporary variable.我给出的例子是我能想到的最简单的例子,它使我的问题与临时变量有关。 I did not ask to get a workaround (proper version) of my example code, but rather to get an answer on my written question.我没有要求获得我的示例代码的解决方法(正确版本),而是要求获得我书面问题的答案。

Nope, because a context manager can't assign variables in the caller's scope like that.不,因为上下文管理器不能像那样在调用者的范围内分配变量。 (Anyone who thinks you could do it with locals or inspect , try using the context manager you come up with inside a function. It won't work.) (任何认为您可以使用localsinspect来做的人,请尝试使用您在函数中提出的上下文管理器。它不会工作。)

There are utilities for doing that with things that aren't local variables, such as module globals, other object attributes, and dicts... but they're unittest.mock.patch and its related functions, so you should strongly consider other alternatives before using them in a non-testing context.一些实用程序可以处理不是局部变量的事情,例如模块全局变量、其他对象属性和字典……但它们是unittest.mock.patch及其相关函数,因此您应该强烈考虑其他替代方案在非测试环境中使用它们之前。 Operations like "temporarily modify this thing and then restore it" tend to lead to confusing code, and may indicate you're using too much global state.像“暂时修改这个东西然后恢复它”这样的操作往往会导致代码混乱,并且可能表明您使用了过多的全局状态。

The simple answer to your question:对您问题的简单回答:

Does the Python standard library contain any such context manager? Python 标准库是否包含任何这样的上下文管理器?

is "No, it does not."是“不,没有。”

My mistake, instead perhaps something like this, it is not built-in:我的错误,也许是这样的,它不是内置的:

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))

Output:输出:

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

For clarity, so you know it's actually doing something:为了清楚起见,你知道它实际上在做一些事情:

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))

Output:输出:

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

You could also do this:你也可以这样做:

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

It should be noted that if you try to use the with statement within a function, you'll want to use globals() as the reference to locals and it may have unintended consequences, might still anyways.应该注意的是,如果您尝试在函数中使用 with 语句,您将希望使用 globals() 作为对局部变量的引用,它可能会产生意想不到的后果,无论如何仍然可能。

I wouldn't recommend doing this at all, but should work.我根本不建议这样做,但应该可行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM