34

withデータベース トランザクションのロジックをブロックにカプセル化しようとしています。コードをトランザクションにラップし、さまざまな例外を処理します (ロックの問題)。これは非常に簡単ですが、特定の例外に続くコード ブロックの再試行をブロックにカプセル化することも必要です。これをコンテキストマネージャーにきちんとパッケージ化する方法がわかりません。

withステートメント内でコードを繰り返すことは可能ですか?

このままシンプルに使いたいと思います。

def do_work():
    ...
    # This is ideal!
    with transaction(retries=3):
        # Atomic DB statements
        ...
    ...

私は現在これをデコレーターで処理していますが、コンテキストマネージャー (または実際には両方) を提供したいのでwith、デコレーターでラップされたインライン関数ではなく、ブロックで数行のコードをラップすることを選択できます、これは私が現在行っていることです:

def do_work():
    ...
    # This is not ideal!
    @transaction(retries=3)
    def _perform_in_transaction():
        # Atomic DB statements
        ...
    _perform_in_transaction()
    ...
4

5 に答える 5

13

withステートメント内でコードを繰り返すことは可能ですか?

いいえ。

そのメーリング リスト スレッドで以前に指摘したように、渡された関数をデコレータで呼び出すことにより、重複を少し減らすことができます。

def do_work():
    ...
    # This is not ideal!
    @transaction(retries=3)
    def _perform_in_transaction():
        # Atomic DB statements
        ...
    # called implicitly
    ...
于 2013-06-04T13:57:06.133 に答える
6

これを行うために私が思いつく方法は、標準のデータベース トランザクションコンテキスト マネージャーretriesを実装するだけで、コンストラクターで引数を取ることができるようにすることです。次に、それをメソッドの実装にまとめます。このようなもの:

class transaction(object):
    def __init__(self, retries=0):
        self.retries = retries
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, traceback):
        pass

    # Implementation...
    def execute(self, query):
        err = None
        for _ in range(self.retries):
            try:
                return self._cursor.execute(query)
            except Exception as e:
                err = e # probably ought to save all errors, but hey
        raise err

with transaction(retries=3) as cursor:
    cursor.execute('BLAH')
于 2013-06-04T14:00:49.950 に答える
4

デコレータは単なる関数そのものであるため、次のことができます。

with transaction(_perform_in_transaction, retries=3) as _perf:
    _perf()

詳細については、元のメソッドを呼び出し、失敗した回数まで繰り返すように設定されtransaction()たオブジェクトを返すファクトリ メソッドとして実装する必要があります。これは、データベース トランザクション コンテキスト マネージャーの標準として定義されます。__callable__()retries__enter__()__exit__()

transaction()別の方法として、渡されたメソッド自体を何度も実行するように設定することもできますretries。これは、おそらくコンテキスト マネージャーの実装とほぼ同じ量の作業が必要になりますが、実際の使用量はtransaction(_perform_in_transaction, retries=3)(実際には、 、提供されているデコレーターの例 delnan と同等)。

于 2013-06-04T13:52:47.853 に答える
0

この質問は数年前のものですが、回答を読んだ後、これを試してみることにしました。

このソリューションでは「ヘルパー」クラスを使用する必要がありますが、コンテキスト マネージャーを介して構成された再試行のインターフェイスを提供すると思います。

class Client:
    def _request(self):
        # do request stuff
        print("tried")
        raise Exception()

    def request(self):
        retry = getattr(self, "_retry", None)
        if not retry:
            return self._request()
        else:
            for n in range(retry.tries):
                try:
                    return self._request()
                except Exception:
                    retry.attempts += 1


class Retry:
    def __init__(self, client, tries=1):
        self.client = client
        self.tries = tries
        self.attempts = 0

    def __enter__(self):
        self.client._retry = self

    def __exit__(self, *exc):
        print(f"Tried {self.attempts} times")
        del self.client._retry


>>> client = Client()
>>> with Retry(client, tries=3):
    ... # will try 3 times
    ... response = client.request()

tried once
tried once
tried once
Tried 3 times
于 2020-08-16T06:41:10.523 に答える