简体   繁体   中英

Python Context Manager to restore a variable's value?

How would you write a context manager to restore a variable to its original value? For example:

x = 5
with Restorer(x):
   x = 6
   ...
print(x)

Output would be: 5

This is only possible under particular circumstances. Usually the context manager only gets the value (actually a reference) of x . It gets no access to the variable itself.

For global variables if the calling function is defined in the same module (or the code is just placed in the same module without surrounding function) the call can be written as

with Restorer('x'):

Then the restorer can be something like (error checking omitted):

class Restorer:

    def __init__(self, varName):
        self.varName = varName

    def __enter__(self):
        self.oldValue = globals()[self.varName]

    def __exit__(self, exc_type, exc_val, exc_tb):
        globals()[self.varName] = self.oldValue

Utilizing contextlib.contextmanager , you can reassign x to its original value after the yield statement:

import contextlib

x = 5
@contextlib.contextmanager
def Restorer(val):
  yield 
  global x
  x = val

print(x)
with Restorer(x):
  x = 6
  print('in contextmanager:', x)

print('outside contextmanager', x)

Output:

5
in contextmanager: 6
outside contextmanager 5

You can abuse a class statement to introduce a temporary "scope":

x = 5
class Foo:
    print(x)
    x = 6
    print(x)
print(x)

This "works" because Foo doesn't establish a true new scope, meaning the first print(x) refers to the currently in-scope name x , but the assignment in a temporary no-man zone in preparation to create a class attribute by the same name, and that attribute is used for the remainder of the body. However, that is, I believe, sufficient to simulate what you are asking for.

(That said, don't actually use this in practice. Write proper functions, or save values manually, if you need some sort of temporary override.)

This is more to do with the scope of names than variables.

You can create scope by creating a function with the behaviour for the block:

def behaviour():
    x = 6
    ...

x = 5
behaviour()
print(x)

Output:

5

Alternatively, you could just introduce a new name:

x = 5
y = 6
...
print(x)

Output:

5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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