2017-11-22 17 views
0

"復帰"デコレータを持つクラスを作成しました。クラスメンバーを歩留まりに変更し、すべての変更を「元に戻す」ために例外が発生した場合、その意図があります。 :ロールバックの目的でクラスデコレータを作成する方法はありますか?

class A(): 
    def __init__(self): 
     self.kuku = 'old_value' 

    @contextmanager 
    def revertible_transaction(self): 
     old_self = deepcopy(self) 
     try: 
      yield 
     except Exception as e: 
      self = old_self 
      raise e 

    def change_stuff(self): 
     with self.revertible_transaction(): 
      self.kuku = 'new_value' 
      raise Exception 

私はchange_stuff()を実行した後にself.kukuはまだ 'OLD_VALUE' になりたいが、それは代わりに 'NEW_VALUE' です。

なぜこれが機能しないのか、これを正しく行うにはどうすればよいですか?

+2

'self = old_self'は実際にオブジェクトを変更しません。 [Pythonでの割り当ての仕組み](https://nedbatchelder.com/text/names.html)を読んでください。 – user2357112

答えて

0

selfに有効に割り当てることはできません。元のオブジェクトを変更せずにローカル名が参照するオブジェクトを変更するだけです。必要に応じて復元するオブジェクトの状態を明示的に保存する必要があります。

@contextmanager 
def revertible_transaction(self): 
    old_kuku = self.kuku 
    try: 
     yield 
    except Exception: 
     self.kuku = old_kuku 
     raise 
0

selfrevertible_transactionは、単なるローカル変数です。 とは別の参照番号change_stuffですが、それ以外のものは変更されません。あなたは(たとえば、yieldによって-ingとwith ... as ...に割り当てる参照渡さ場合でも、あなたはまだchange_stuff()方法は、例えば持っているにかかわらず、参照呼び出し側代わるものではありませ思います。

だから、同じようselfを置き換えることはできません。

@contextmanager 
def revertible_transaction(self): 
    old_state = deepcopy(self.__dict__) 
    try: 
     yield 
    except Exception as e: 
     self.__dict__ = old_state 
     raise e 

これはスロットを使用するクラスでは動作しませんが、コードは自明拡張することができます:あなたは通常、self.__dict__辞書を通じてこれらに到達することができます。これは、あなたはインスタンスが属性を交換する必要がありますすべて__slots__ class attribにリストされているすべての名前をコピーします。 MROのutes。

関連する問題