Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to assign member variables temporarily?

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)

like image 930
MWB Avatar asked Jul 22 '16 17:07

MWB


3 Answers

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
like image 170
Łukasz Rogalski Avatar answered Nov 17 '22 06:11

Łukasz Rogalski


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) 
like image 5
Moses Koledoye Avatar answered Nov 17 '22 06:11

Moses Koledoye


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__)
like image 3
Michael Hoff Avatar answered Nov 17 '22 04:11

Michael Hoff