I often find that I need to assign some member variables temporarily, e.g.
old_x = c.x
old_y = c.y
# keep c.z unchanged
c.x = new_x
c.y = new_y
do_something(c)
c.x = old_x
c.y = old_y
but I wish I could simply write
with c.x = new_x; c.y = new_y:
do_something(c)
or even
do_something(c with x = new_x; y = new_y)
Can Python's decorators or other language features enable this kind of pattern? (I could modify c
's class as needed)
Context managers may be used for it easily.
Quoting official docs:
Typical uses of context managers include saving and restoring various kinds of global state, locking and unlocking resources, closing opened files, etc.
It seems like saving and restoring state is exactly what we want to do here.
Example:
from contextlib import contextmanager
@contextmanager
def temporary_change_attributes(something, **kwargs):
previous_values = {k: getattr(something, k) for k in kwargs}
for k, v in kwargs.items():
setattr(something, k, v)
try:
yield
finally:
for k, v in previous_values.items():
setattr(something, k, v)
class Something(object):
def __init__(self, x, y):
self.x = x
self.y = y
def say_hello(self):
print("hello", self.x, self.y)
s = Something(1, 2)
s.say_hello() # hello 1 2
with temporary_change_attributes(s, x=4, y=5):
s.say_hello() # hello 4 5
s.say_hello() # hello 1 2
I think a contextmanager
should do what you want:
from contextlib import contextmanager
@contextmanager
def current_instance(c, temp_x, temp_y):
old_x, old_y = c.x, c.y
c.x, c.y = temp_x, temp_y
yield c
c.x, c.y = old_x, old_y
with current_instance(c, x, y) as c_temp:
do_something(c_temp)
You can also do this natively using __enter__
and __exit__
. Simplistic example:
class SomeObject(object):
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
class Temporary(object):
def __init__(self, target, **kv):
self.target = target
self.to_set = kv
self.to_restore = {}
def __enter__(self):
self.to_restore = map(partial(getattr, self.target), filter(partial(hasattr, self.target), self.to_set.keys()))
for k,v in self.to_set.items():
if hasattr(self.target, k):
self.to_restore[k] = getattr(self.target, k)
setattr(self.target, k, v)
def __exit__(self, *_):
for k,v in self.to_restore.items():
setattr(self.target, k, v)
for k in self.to_set.keys():
if k not in self.to_restore:
delattr(self.target, k)
o = SomeObject()
print(o.__dict__)
with Temporary(o, a=42, d=1337):
print(o.__dict__)
print(o.__dict__)
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