确定是否从with语句调用

时间:2014-12-09 09:07:35

标签: python

我有一个可以直接调用或通过with语句调用的函数,它的行为应该根据它的调用方式而有所不同,如果通过with语句调用则返回上下文管理器,否则执行上下文管理器会执行的操作直接做。

e.g。

class Context(object):
    def __enter__(self):
        print('start')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('end')
        return False


def myfunc():
    if called_from_with:
        return Context()
    else:
        print('start')
        print('end')

从with:

调用时
>>> with myfunc():
>>>    print('foo')

start
foo
end

直接打电话:

>>> myfunc()

start
end

有办法做到这一点吗?

编辑:我的用例是我正在编写的用于编写html的工具。我在这里写了我的初始代码:https://github.com/garyvdm/htmlwrite(请注意它目前正在进行中。)

我希望用户能够写下这个:

writer = Writer(sys.stdout)

with writer(Tag('div')):
    writer('Hello world')

writer(Tag('div', c=("Hello world 2", )))

而不是:(注意对.wrapped的调用)

writer = Writer(sys.stdout)

with writer.wrapped(Tag('div')):
    writer('Hello world')

writer(Tag('div', c=("Hello world 2", )))

2 个答案:

答案 0 :(得分:1)

假设您要避免的是双上下文管理器调用,同一个上下文管理器不应该加倍,我建议你破解上下文管理器是可重入的。

以下是使用交易的简单示例:

@contextmanager()
def foo(self):
    # if object can be shared across threads,
    # make sure only one thread can enter at a time
    # alternatively, inherit from threading.Local
    with self.lock:
        if self._transaction:
            yield
        else:
            try:
                with real_transaction() as self._transaction:
                    yield
            finally:
                self._transaction = None

答案 1 :(得分:0)

上下文管理器不仅限于with语句。除了在将变量管理器用于withcm = writer(Tag('div')),然后with cm:)之前将其分配给变量之外,您还可以在ExitStack中使用上下文管理器或调用{{1和/ __enter__方法分开。

更好的选择是始终从您的函数返回上下文管理器。如果这意味着您还需要提供不同的函数来提供非上下文管理器版本的行为,那就这样吧。

那就是说,你可以根据参数的数量调整行为;这是TestCase.assertRaises()的作用。在内部,您总是创建一个上下文管理器,但是如果有更多参数,您只需立即应用上下文:

__exit__