1

私は、リモート オブジェクト (クローンを操作するために複製できないオブジェクト) の変更を命令するコードの一部を操作している状況に遭遇しました。新しい状態にして、一連の反対のコマンドで行ったすべての変更を元に戻します。問題は、これらすべての変更の途中でエラーが発生した場合、これまでに行ったすべての変更をロールバックできるようにしたいということです。

私の頭に浮かんだ最適な解決策は、Python の try-finally ワークフローですが、コマンドのシーケンスが長い場合はかなり問題になります。

try:
    # perform action
    try:
        # perform action
        try: 
            # ...
        finally:
            # unroll
    finally:
        # unroll
finally:
    # unroll

このように、必要なコマンドが増えるほど、インデントとネストが深くなり、コードが読みにくくなります。コマンドごとにロールバックアクションをプッシュするスタックを維持するなど、他の解決策をいくつか検討しましたが、これはかなり複雑になる可能性があり、バインドされたメソッドをスタックにプッシュするのは嫌いです。また、実行するアクションごとにカウンターをインクリメントすることも検討し、最終的にカウンターに応じて必要なロールバックの種類を決定しますが、そのようなコードの保守性は苦痛になります.

「トランザクション」と「ロールバック」の検索で得たほとんどのヒットはDB関連であり、より一般的な種類のコードにはあまり適合しませんでした.この残虐行為を体系的に平坦化する方法について誰かが良い考えを持っていますか?

4

1 に答える 1

5

Context Manager オブジェクトwithステートメントで状況が改善されるのではないでしょうか? 特に、2.7 または 3.x のように with ステートメントが複数のコンテキスト式をサポートするバージョンの Python を使用できる場合。次に例を示します。

class Action(object):
    def __init__(self, count):
        self.count = count
    def perform(self):
        print "perform " + str(self.count)
        if self.count == 2:
            raise Exception("self.count is " + str(self.count))
    def commit(self):
        print "commit " + str(self.count)
    def rollback(self):
        print "rollback " + str(self.count)
    def __enter__(self):
        perform()
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_value is None:
            self.commit()
        else:
            self.rollback()

with Action(1), Action(2), Action(3):
    pass

上記のような「トランザクション」クラスのセットにコードを移動する必要があります。Action実行されるアクションはメソッドで実行されます。これが正常に終了した場合、対応するメソッドが呼び出されること__enter__()が保証されます。__exit()__.

私の例はあなたのものと正確に対応していないことに注意してください。__enter__()メソッドで何を実行し、withステートメントの本体で何を実行するかを調整する必要があります。その場合、次の構文を使用することをお勧めします。

with Action(1) as a1, Action(2) as a2:
    pass

ステートメントActionの本文内からオブジェクトにアクセスできるようにするため。with

于 2011-08-19T09:27:13.107 に答える