Python 跳过带块的执行

Python 跳过带块的执行,python,with-statement,skip,Python,With Statement,Skip,我正在定义一个上下文管理器类,如果在实例化过程中满足某些条件,我希望能够跳过代码块而不引发异常。比如说, 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 __en

我正在定义一个上下文管理器类,如果在实例化过程中满足某些条件,我希望能够跳过代码块而不引发异常。比如说,

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

不幸的是,你想做的是不可能的。如果
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
引发异常,则在
with
语句中引发该异常(
\uuuuuuuuuuuuuu
不被调用)。如果它没有引发异常,那么返回值将被提供给块并执行块

我能想到的最接近的事情是由块显式检查的标志:

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

这也允许您在<<代码>的中间,用块来模拟一个正常的<代码>中断< /C> >语句。< /P> < P>根据,<代码>带有< /Cord>语句从

with EXPR as VAR:
    BLOCK
致:

如您所见,通过调用上下文管理器的
\uuuu enter\uuuu()
方法,可以跳过with语句的主体(

人们已经完成了Python实现特有的工作,例如在诸如以下项目中操纵
\uuuu enter\uuuu()
中的调用堆栈。我记得Alex Martelli在一两年前在stackoverflow上发布了一篇非常有趣的关于hack的文章(不要立即回忆起足够多的文章来搜索和找到它)


但对您的问题的简单回答是,如果不借助所谓的“深层魔法”(不一定可以在python实现之间移植),您就无法做您要做的事情,跳过with语句的主体。使用deep magic,您可能可以做到这一点,但我建议您只做一些练习,看看如何做到这一点,而不要在“生产代码”中进行。如果您想要一个使用来自(特别是来自)的想法的特别解决方案,这将起作用:

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


Python3更新了其他来自 (特别是来自:

类SkipWithBlock(异常):
通过
类SkipContextManager:
定义初始化(自我,跳过):
self.skip=跳过
定义输入(自我):
如果self.skip:
sys.settrace(lambda*args,**键:无)
frame=sys.\u getframe(1)
frame.f_trace=self.trace
def跟踪(自身、帧、事件、参数):
提升SkipWithBlock()
定义退出(自身、类型、值、回溯):
如果类型为“无”:
返回#无例外
如果issubclass(类型,SkipWithBlock):
返回True#抑制特殊SkipWithBlock异常
使用SkipContextManager(skip=True):
不会调用print('In the with block')#
打印('带块输出')
正如joe之前提到的,这是一种应该避免的黑客行为:

方法trace()在输入新的本地作用域时调用,即在with块中的代码开始时调用。当在此处引发异常时,它会被退出捕获。这就是黑客的工作原理。我要补充的是,这是一个非常黑客,不应该依赖。神奇的sys.settrace()实际上不是语言定义的一部分,它只是碰巧在CPython中。此外,调试器依赖sys.settrace()完成其工作,因此自己使用它会干扰这一点。您不应该使用此代码的原因有很多。仅供参考


另一个略显粗糙的选项使用了
exec
。这很方便,因为可以修改它来执行任意操作(例如,记忆上下文块):


如果你能想出一个办法,让我洗耳恭听,那就好了。这个问题的答案似乎是这样的,但有。

根据@Peter的答案,这里有一个版本,它不使用字符串操作,但在其他情况下应该以相同的方式工作:

from contextlib import contextmanager

@contextmanager
def skippable_context(skip):
    skip_error = ValueError("Skipping Context Exception")
    prev_entered = getattr(skippable_context, "entered", False)
    skippable_context.entered = False

    def command():
        skippable_context.entered = True
        if skip:
            raise skip_error

    try:
        yield command
    except ValueError as err:
        if err != skip_error:
            raise
    finally:
        assert skippable_context.entered, "Need to call returned command at least once."
        skippable_context.entered = prev_entered


print("=== Running with skip disabled ===")
with skippable_context(skip=False) as command:
    command()
    print("Entering this block")
print("... Done")

print("=== Running with skip enabled ===")
with skippable_context(skip=True) as command:
    command()
    raise NotImplementedError("... But this will never be printed")
print("... Done")


上下文管理器不是用于此的正确构造。你要求尸体被执行n次,在这种情况下是零次或一次。如果查看一般情况n,其中n>=0,则会得到一个for循环:

def do_squares(n):
  for i in range(n):
    yield i ** 2

for x in do_squares(3):
  print('square: ', x)

for x in do_squares(0):
  print('this does not print')
在您的情况下,这是一个更特殊的用途,并且不需要绑定到循环变量:

def should_execute(mode=0):
  if mode == 0:
    yield

for _ in should_execute(0):
  print('this prints')

for _ in should_execute(1):
  print('this does not')

我发现了这个,但我不知道如何理解它,也不知道如何实现它。还有其他更优雅的方式吗?这是一个政治公众人物(以及对语义变化的讨论)的事实表明,如果不改变口译员的行为,就无法实现它使用A(),B():B的enter可以引发一些事情,我觉得很好。我也需要这个功能。但以非黑客方式实现这一点的唯一建议是在python中添加一个新的系统异常。这个建议是因为它被认为语言中额外复杂性的成本不值得这么做。这个标志是有效的,但我希望在上下文管理器中保留所有检查,并保持代码块干净。如果这是不可能的,我可能不得不找到另一种方法,除了和你一起。非常感谢你!好的,这解释了很多。我和哈克结帐了。我想这是我无法理解的。。。。我还不确定如何使用这些代码来执行跳过,但我肯定可以使用一些有趣的代码片段。(更新:RUBY风格的块?我明白了,哈哈哈。那真是疯了)否则,我真的需要考虑另一种方法。非常感谢。这就是我要找的。我明白了,所以它以某种方式触发了一个TypeError,它被_exit__()方法捕获并抑制。有趣的工作!我在方法uuu exit uuuu()中添加了一个if循环来检查类型和值,以便只抑制黑客引发的异常。当输入新的本地作用域时,即当
with
块中的代码开始时,调用方法
trace()
。当在此处引发异常时,它会被
\uuuu exit\uuuu()
捕获。这就是黑客的工作原理。我应该补充说,这是一个非常黑客,不应该依赖于美国
from contextlib import contextmanager

@contextmanager
def skippable_context(skip):
    skip_error = ValueError("Skipping Context Exception")
    prev_entered = getattr(skippable_context, "entered", False)
    skippable_context.entered = False

    def command():
        skippable_context.entered = True
        if skip:
            raise skip_error

    try:
        yield command
    except ValueError as err:
        if err != skip_error:
            raise
    finally:
        assert skippable_context.entered, "Need to call returned command at least once."
        skippable_context.entered = prev_entered


print("=== Running with skip disabled ===")
with skippable_context(skip=False) as command:
    command()
    print("Entering this block")
print("... Done")

print("=== Running with skip enabled ===")
with skippable_context(skip=True) as command:
    command()
    raise NotImplementedError("... But this will never be printed")
print("... Done")

def do_squares(n):
  for i in range(n):
    yield i ** 2

for x in do_squares(3):
  print('square: ', x)

for x in do_squares(0):
  print('this does not print')
def should_execute(mode=0):
  if mode == 0:
    yield

for _ in should_execute(0):
  print('this prints')

for _ in should_execute(1):
  print('this does not')