有没有办法阻止上下文管理器进入`with`主体?

时间:2015-09-02 00:56:57

标签: python

我试过了:

@contextmanager
def changed_dir(dirname, msg="nothing to do.."):
    if changed(dirname):
        yield
    else:
        print msg
    return

然而,当我尝试使用它时:

with changed_dir("foo/bar"):
    print "something in foo/bar was changed"

我受到了欢迎:

RuntimeError: generator didn't yield

有什么方法可以让它发挥作用吗?

更新:很多人似乎都被这个例子简单化了。这是一个更复杂的例子,说明了同一点

@contextmanager
def changed_dir(dirname, msg="..."):
    cn = db.get_connection(...)
    try:
        cn.execute("insert ...")
        if changed(dirname):
            cn.execute(...)
            yield
            os.mkdirs(os.path.join('backup', dirname))
            # copy the modified tree.. 
            # etc.
        else:
            cn.execute(...)
            print msg
        cn.commit()
    except:
        cn.rollback()
        # cleanup from mkdirs call...
    finally:
        cn.close()

是否仍然是上述唯一的解决方案?

2 个答案:

答案 0 :(得分:1)

Guido在PEP-0343中的评论似乎表明这个用例是copacetic,并且基于我提出的pep示例(我查看了来自@ Cyphase的答案的链接,但这似乎不必要地复杂化):< / p>

class changed_dir(object):
    class NoChange(ValueError):
        pass

    def __init__(self, dirname, msg="nothing to do"):
        self.dirname = dirname
        self.msg = msg

    def __enter__(self):
        if changed(self.dirname):
            return
        else:
            print self.msg
            raise changed_dir.NoChange()

    def __exit__(self, type, value, traceback):
        return isinstance(value, changed_dir.NoChange)

这看起来像大多数上下文管理器一样简单(但不像我可以使用装饰器那样简单。)

答案 1 :(得分:0)

It's possible,但你可能不应该这样做。使用if/else。实际上,只需使用上下文管理器中的if/else

msg = "nothing to do.."

if changed(dirname):
    print "something in foo/bar was changed"
else:
    print msg

<强>更新

在回复您的更新时,我会如何做到这一点:

@contextmanager
def db_context_manager():  # Name this better
    cn = db.get_connection(...)
    try:
        cn.execute("insert ...")
        yield
        cn.commit()
    except:
        cn.rollback()
    finally:
        cn.close()

with db_context_manager():
    if changed(dirname):
        do_stuff()