Very easy using context managers!

The functions locals() and globals() return dictionaries containing the variables that are defined in the current local or global scope. What is cool is that variables can be declared, modified, and “undeclared” by modifying these dictionaries (see a tutorial here)!

To get a temporary variable, we can therefore build a context manager that adds and removes a variable to either the local or the global definitions, for example:

class TemporaryVariables:
    def __init__(self, dest, **kwargs):
        # dest is a dictionary, either from locals() or globals()
        # kwargs are the variables to define, and their values
        self._vars = kwargs
        self._old = dest
        self._backup = {}
        for k, v in self._vars.items():
            # for each variable...
            if k in self._old:
                # store the old value if overwriting
                self._backup[k] = self._old[k]
            # and set the new value
            self._old[k] = v

    def __enter__(self, *args, **kwargs):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        for k, v in self._vars.items():
            # for each variable...
            if k in self._backup:
                # restore the old value if it was overwritten
                self._old[k] = self._backup[k]
            else:
                # or "undefine" the variable if it was new
                self._old.pop(k)

Here’s how you would use this:

a = 'hello'
print(a)
with TemporaryVariables(locals(), a='world'):
    a = a + '!'
    print(a)
print(a)

Which prints:

hello
world!
hello

Due to the way we took the backup, variables that were not defined before the with block remain undefined after it, too:

with TemporaryVariables(locals(), x=42):
    print(x)
print(x)  # x was only defined in the with block

We now get an error when using x after whe with:

42

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In [8], line 3
      1 with TemporaryVariables(locals(), x=42):
      2     print(x)
----> 3 print(x)

NameError: name 'x' is not defined

Finally, notice the difference between local and global scoping:

def f():
    print('inside f with b =', b)


def scope_test():
    with TemporaryVariables(globals(), b=2) as q:
        f()

    with TemporaryVariables(locals(), b=2) as q:
        f()

scope_test()

Now, only the variable b that is defined in the global scope can be used in functions called from within the with:

inside f, with b = 2

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

Cell In [9], line 13
     10     with TemporaryVariables(locals(), b=2) as q:
     11         f()
---> 13 scope_test()


Cell In [9], line 11, in scope_test()
      7     f()
     10 with TemporaryVariables(locals(), b=2) as q:
---> 11     f()


Cell In [9], line 2, in f()
      1 def f():
----> 2     print('inside f, with b =', b)


NameError: name 'b' is not defined

Happy hacking!