-with-块的跳过执行(Skipping execution of -with- block)

2019-07-31 22:36发布

我定义上下文管理类,我希望能够跳过的代码块,如果没有实例化过程中在满足某些条件引发异常。 例如,

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...'

Answer 1:

如果你想使用来自思想的临时解决方案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 ... '


Answer 2:

根据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)

正如你所看到的,没有什么明显的可以从调用做__enter__()上下文管理器,可以跳过体(“法BLOCK的”)的声明。

人们已经做了具体的Python实现的东西,比如操纵的内部调用堆栈__enter__()等项目withhacks 。 我记得亚历克斯·马尔泰利张贴计算器一年两回(不记得足够后离手的搜索和找到它)一个很有意思的用下锅。

但简单的回答你的问题/问题是,你不能做什么你问,跳绳with语句身体,而不诉诸所谓的“冥神功”(这不一定是Python实现之间移植)。 凭借深厚的魔法,你也许可以做到这一点,但我建议只是在做这样的事情作为一个练习中看到它会怎么做,从来没有在“生产代码”。



Answer 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声明。



Answer 4:

一个Python 3更新于由其他的答案从提及的劈withhacks (具体为AnonymousBlocksInPython ):

class SkipWithBlock(Exception):
    pass


class SkipContextManager:
    def __init__(self, skip):
        self.skip = skip

    def __enter__(self):
        if self.skip:
            sys.settrace(lambda *args, **keys: None)
            frame = sys._getframe(1)
            frame.f_trace = self.trace

    def trace(self, frame, event, arg):
        raise SkipWithBlock()

    def __exit__(self, type, value, traceback):
        if type is None:
            return  # No exception
        if issubclass(type, SkipWithBlock):
            return True  # Suppress special SkipWithBlock exception


with SkipContextManager(skip=True):    
    print('In the with block')  # Won't be called
print('Out of the with block')

正如乔前面提到的,这是应该避免的黑客攻击:

进入一个新的本地范围时,在你的代码与块开始时的方法跟踪()被调用,即右。 ()当一个异常被这里提出它就会被退出抓获。 这就是这个黑客是如何工作的。 我要补充的是,这是一个很有黑客,不应加以依赖。 神奇sys.settrace()实际上不是语言定义的一部分,它恰好是在CPython的。 此外,调试器依靠sys.settrace()做他们的工作,因此使用它自己的干扰这一点。 有很多原因,你不应该使用这个代码。 仅供参考。



文章来源: Skipping execution of -with- block