上下文管理器反向

时间:2015-12-03 21:26:15

标签: python with-statement contextmanager

我在Python的中级水平,我最近一直在玩Python上下文管理器。我想反转运行输入和退出的顺序。所以我写了这个上下文管理器:

class ReversibleContextManager(object):

    def __enter__(self, *args):
        print('d')
        return self

    def __exit__(self, *args):
        print('g')
        return self

    def __invert__(self):
        self.__enter__, self.__exit__ = self.__exit__, self.__enter__
        return self

它的工作正常:

with ContextManager():
    print('o')

d
o
g

但相反,我们仍然得到:

with ~ContextManager():
    print('a')

d
o
g

如果我们按预期显式调用enter和exit函数,我们得到:

with ReversibleContextManager() as c:
    c.__enter__()
    print('o')
    c.__exit__()

d
d
o
g
g

但是对于实例的方法,订单IS是相反的!

with ~ReversibleContextManager() as c:
    c.__enter__()
    print('o')
    c.__exit__()

d
g
o
d
g

所以看起来使用绑定到Class而不是实例的方法的with语句调用(这是正确的术语吗?)。这是预期的吗?

所谓的是:

c = ReversibleContextManager()
c.__invert__()
ReversibleContextManager.__enter__(c)
...in context...
ReversibleContextManager.__exit__(c)

而不是我的期望:

c = ReversibleContextManager()
c.__invert__()
c.__enter__()
...in context...
c.__exit__()

4 个答案:

答案 0 :(得分:2)

  

所以看起来使用绑定到Class而不是实例的方法的with语句调用(这是正确的术语吗?)。这是预期的吗?

绝对。这就是Python一般查找特殊方法的方式。如果您有一个Foo类来实现__str__print Foo调用type(Foo).__str__而不是Foo.__str__,则会出现这种情况。

答案 1 :(得分:2)

作为一种解决方法,您可以在__invert__函数中创建反向类,并返回新类的实例。

class ReversibleContextManager(object):
    def __enter__(self, *args):
        print('enter')
        return self
    def __exit__(self, *args):
        print('exit')
        return self
    def __invert__(self):
        new = type("ReversibleContextManager",
                   (object,),
                   {'__enter__': self.__exit__,
                    '__exit__': self.__enter__})
        return new()

>>> with ReversibleContextManager() as f:
...     print("normal")
enter
normal
exit

>>> with ~ReversibleContextManager() as f:
...     print("reversed")
exit
reversed
enter

答案 2 :(得分:1)

一个更详细但非常明确的方法是使用两个辅助函数,只需切换哪个进入和退出一个标记reverse

class ReversibleContextManager(object):

    def __init__(self, reverse=False):
        self.reverse = reverse

    def _enter(self, *args):
        print('d')
        return self

    def _exit(self, *args):
        print('g')
        return self

    def __enter__(self, *args):
        if self.reverse:
            return self._exit(*args)
        return self._enter(*args)

    def __exit__(self, *args):
        if self.reverse:
            return self._enter(*args)
        return self._exit(*args)

    def __invert__(self):
        self.reverse = True
        return self


>>> with ReversibleContextManager() as r:
    print('o')
d
o
g

>>> with ~ReversibleContextManager() as r:
    print('o')
g
o
d

>>> with ReversibleContextManager(reverse=True) as r:
    print('o')
g
o
d

答案 3 :(得分:0)

另一种方式:

class ReversibleContextManager(object):

    inverted = False

    def __init__(self):
        if self.__class__.inverted:
            self.__invert__()
            self.__class__.inverted = False

    def __enter__(self, *args):
        print('d')
        return self

    def __exit__(self, *args):
        print('g')
        return self

    def __invert__(self):
        self.__class__.inverted = True
        self.__class__.__enter__, self.__class__.__exit__ = self.__exit__, self.__enter__
        return self