我在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__()
答案 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