Python 3.4 provides this neat tool to temporarily redirect stdout:
# From https://docs.python.org/3.4/library/contextlib.html#contextlib.redirect_stdout
with redirect_stdout(sys.stderr):
help(pow)
The code is not super-complicated, but I wouldn't want to write it over and over again, especially since some thought has gone into it to make it re-entrant:
class redirect_stdout:
def __init__(self, new_target):
self._new_target = new_target
# We use a list of old targets to make this CM re-entrant
self._old_targets = []
def __enter__(self):
self._old_targets.append(sys.stdout)
sys.stdout = self._new_target
return self._new_target
def __exit__(self, exctype, excinst, exctb):
sys.stdout = self._old_targets.pop()
I'm wondering if there's a general way to use the with
statement to temporarily change the value of a variable. Two other use cases from sys
are sys.stderr
and sys.excepthook
.
In a perfect world, something like this would work:
foo = 10
with 20 as foo:
print(foo) # 20
print (foo) # 10
I doubt we can make that work, but maybe something like this is possible:
foo = 10
with temporary_set('foo', 20):
print(foo) # 20
print (foo) # 10
I can sort of getting this working by rooting around in globals()
, but it's nothing anyone would choose to use.
UPDATE: while I think my "foo = 10" examples clarified what I'm trying to do, they do not convey an actual use case. Here are two:
In Variables view, right-click the variable whose value you want to change and select Change Value.
The simplest way to swap the values of two variables is using a temp variable. The temp variables is used to store the value of the fist variable ( temp = a ). This allows you to swap the value of the two variables ( a = b ) and then assign the value of temp to the second variable.
Reassigning variablesWe can reassign variable values in Python as follows. The reassignment works similarly to how we create the variable. We only provide a different value than what was stored before. Here, the initial value of a was 10 , which was reassigned to 20 from the second line in the above code block.
I know this question is kind of old, but as I came around the same problem, here is my solution:
class test_context_manager():
def __init__(self, old_object, new_object):
self.new = new_object
self.old = old_object
self.old_code = eval(old_object)
def __enter__(self):
globals()[self.old] = self.new
def __exit__(self, type, value, traceback):
globals()[self.old] = self.old_code
It's not pretty as it makes heavy use of global variables, but it seems to work.
For example:
x = 5
print(x)
with test_context_manager("x", 7):
print(x)
print(x)
Result:
5
7
5
or with functions:
def func1():
print("hi")
def func2():
print("bye")
x = 5
func1()
with test_context_manager("func1", func2):
func1()
func1()
Result:
hi
bye
hi
I normally use this custom attr_as
context manager:
from contextlib import contextmanager
@contextmanager
def attr_as(obj, field:str, value) -> None:
old_value = getattr(obj, field)
setattr(obj, field, value)
yield
setattr(obj, field, old_value)
You can then use it with the exact same set of arguments you'd use for setattr
in attr_as
.
class Foo:
def __init__(self):
self.x = 1
foo = Foo()
with attr_as(foo, 'x', 2):
print(foo.x)
bar = 3
with attr_as(sys.modules[__name__], 'bar', 4):
print(bar)
Note, if you need to preserve the existence/nonexistence of the attribute rather than just the value, that's also possible with just a few more lines:
from contextlib import contextmanager
@contextmanager
def attr_as(obj, field:str, value) -> None:
old_exists = hasattr(obj, field)
if old_exists:
old_value = getattr(obj, field)
setattr(obj, field, value)
yield
if old_exists:
setattr(obj, field, old_value)
else:
delattr(obj, field)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With