32

コンテキスト マネージャー クラスを定義していますが、インスタンス化中に特定の条件が満たされた場合に、例外を発生させずにコード ブロックをスキップできるようにしたいと考えています。例えば、

class My_Context(object):
    def __init__(self,mode=0):
        """
        if mode = 0, proceed as normal
        if mode = 1, do not execute block
        """
        self.mode=mode
    def __enter__(self):
        if self.mode==1:
            print 'Exiting...'
            CODE TO EXIT PREMATURELY
    def __exit__(self, type, value, traceback):
        print 'Exiting...'

with My_Context(mode=1):
    print 'Executing block of codes...'
4

7 に答える 7

23

withhacks(特にAnonymousBlocksInPython)のアイデアを使用するアドホックソリューションが必要な場合は、次のように機能します。

import sys
import inspect

class My_Context(object):
    def __init__(self,mode=0):
        """
        if mode = 0, proceed as normal
        if mode = 1, do not execute block
        """
        self.mode=mode
    def __enter__(self):
        if self.mode==1:
            print 'Met block-skipping criterion ...'
            # Do some magic
            sys.settrace(lambda *args, **keys: None)
            frame = inspect.currentframe(1)
            frame.f_trace = self.trace
    def trace(self, frame, event, arg):
        raise
    def __exit__(self, type, value, traceback):
        print 'Exiting context ...'
        return True

以下を比較してください。

with My_Context(mode=1):
    print 'Executing block of code ...'

with My_Context(mode=0):
    print 'Executing block of code ... '
于 2012-09-26T04:56:36.833 に答える
23

PEP-343によると、withステートメントは次から変換されます。

with EXPR as VAR:
    BLOCK

に:

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

ご覧のとおり、with ステートメント__enter__()の本体 (" ") をスキップできるコンテキスト マネージャーのメソッドへの呼び出しからできることは明らかではありません。BLOCK

人々は、withhacks__enter__()などのプロジェクトで、内のコール スタックを操作するなど、Python 実装に固有のことを行ってきました。Alex Martelli が 1 年か 2 年前に、stackoverflow に非常に興味深い with-hack を投稿したことを思い出します (検索して見つけるのに十分な量の投稿を思い出す必要はありません)。

しかし、あなたの質問/問題に対する簡単な答えは、いわゆる「ディープマジック」に頼らなければ、 with ステートメントの本文をスキップして、求めていることを実行できないということです(これは、Python実装間で必ずしも移植可能ではありません)。深い魔法があればできるかもしれませんが、「本番コード」ではなく、どのように実行されるかを確認するための演習などのみを行うことをお勧めします。

于 2012-09-26T03:54:27.110 に答える
3

残念ながら、あなたがやろうとしていることは不可能です。例外が発生した場合__enter__、その例外はwithステートメントで発生します (__exit__呼び出されません)。例外が発生しない場合は、戻り値がブロックに渡され、ブロックが実行されます。

私が考えることができる最も近いものは、ブロックによって明示的にチェックされるフラグです:

class Break(Exception):
    pass

class MyContext(object):
    def __init__(self,mode=0):
        """
        if mode = 0, proceed as normal
        if mode = 1, do not execute block
        """
        self.mode=mode
    def __enter__(self):
        if self.mode==1:
            print 'Exiting...'
        return self.mode
    def __exit__(self, type, value, traceback):
        if type is None:
            print 'Normal exit...'
            return # no exception
        if issubclass(type, Break):
            return True # suppress exception
        print 'Exception exit...'

with MyContext(mode=1) as skip:
    if skip: raise Break()
    print 'Executing block of codes...'

Break()これにより、ブロックの途中でレイズして、通常のステートメントwithをシミュレートすることもできます。break

于 2012-09-26T03:50:40.827 に答える