Search code examples
pythonpython-2.7python-decoratorspython-descriptors

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)


Solution

  • 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