2016-07-22 21 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 

が、Pythonのデコレータや他の言語機能は、パターンのこの種のを可能にすることができます私は、私は単に

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

あるいは

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

を書くことがしたいですか? (私は必要に応じてcのクラスを変更できます)

+2

はなぜ新しいインスタンスを作成しない – haifzhan

+0

@HaifengZhangを 'C'は、多くの他の変数を持っているだろうか?は変更されません – MaxB

+0

'do_something(c)'が 'c'の他の属性に代入しない限り、@HaifengZhangは良い呼び出しです。 –

答えて

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 
関連する問題