2016-07-22 157 views
12

我经常发现我需要暂时分配一些成员变量,例如如何临时分配成员变量?

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 

,但我想我可以简单的写

with c.x = new_x; c.y = new_y: 
    do_something(c) 

甚至

do_something(c with x = new_x; y = new_y) 

能Python的装饰或其他语言的特性使这种模式? (我可以根据需要修改c的类)

+2

为什么不创建一个新的实例 – haifzhan

+0

@HaifengZhang'C'可以有许多其他的变量?没有修改 – MaxB

+0

好的@HaifengZhang,除非'do_something(c)'指定给'c'的其他属性。 –

回答

20

Context managers可用于很容易。

引述官方的文档:上下文管理的

典型用途包括保存和恢复各种 种全局状态,锁定和解锁资源,关闭打开 文件等

似乎像保存和恢复状态正是我们在这里要做的。

例子:

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 
+7

这是一个很好的答案,但是您可能想要将上下文管理器的内部封装在“try/finally” – acushner

3

mock提供此功能,具体查看patch.object的上下文管理器使用情况。它位于python3的核心库中,旧版python的available on pypi

设置:

>>> class C: 
...  def __init__(self, x, y, z): 
...   self.x = x 
...   self.y = y 
...   self.z = z 
...   
>>> c = C(0,1,2) 

用法演示:

>>> print(c.x, c.y, c.z) 
0 1 2 
>>> with patch.object(c, 'x', 'spam'), patch.object(c, 'y', 'eggs'): 
...  print(c.x, c.y, c.z) 
...  
spam eggs 2 
>>> print(c.x, c.y, c.z) 
0 1 2 
5

我觉得应该contextmanager做你想要什么:

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) 
3

你也可以做到这一点本身使用__enter____exit__。简单的例子:

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__) 
2

高飞解决方案

>>> class Foo(object): 
     def __init__(self): 
      self._x = [] 
      self._y = [] 


     @property 
     def x(self): 
      return self._x[-1] or None 

     @x.setter 
     def x(self, val): 
      self._x.append(val) 

     def reset_vals(self): 
      if len(self._x) > 1: 
       self._x.pop() 


>>> bar = Foo() 
>>> bar.x = 1 
>>> bar.x 
1 
>>> bar.x = 2 
>>> bar.x 
2 
>>> bar.reset_vals() 
>>> bar.x 
1 
>>> bar.reset_vals() 
>>> bar.x 
1 

还是愚蠢的,但没有那么解决方案

>>> class Foo(object): 
    def __init__(self): 
     pass 


>>> import copy 
>>> bar = Foo() 
>>> bar.x = 1 
>>> bar.x 
1 
>>> bar2 = copy.copy(bar) 
>>> bar2.x 
1 
>>> bar2.x = 5 
>>> bar2.x 
5 
>>> bar 
<__main__.Foo object at 0x0426A870> 
>>> bar.x 
1