上下文管理器反向
Context Manager Reverse
我在 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
如果我们按预期显式调用进入和退出函数,我们将得到:
with ReversibleContextManager() as c:
c.__enter__()
print('o')
c.__exit__()
d
d
o
g
g
但是实例方法的顺序是相反的!
with ~ReversibleContextManager() as c:
c.__enter__()
print('o')
c.__exit__()
d
g
o
d
g
所以看起来 with 语句调用使用绑定到 Class 的方法而不是实例(这是正确的术语吗?)。这是预期的吗?
即
所谓的是:
c = ReversibleContextManager()
c.__invert__()
ReversibleContextManager.__enter__(c)
...in context...
ReversibleContextManager.__exit__(c)
而不是我所期望的:
c = ReversibleContextManager()
c.__invert__()
c.__enter__()
...in context...
c.__exit__()
So it looks like the with statement calls using the method bound to the Class rather than the instance (is this the right terminology?). Is this expected?
当然可以。这就是 Python 通常查找特殊方法的方式。如果你有一个 class Foo
实现了 __str__
,print Foo
调用 type(Foo).__str__
而不是 Foo.__str__
。
作为解决方法,您可以在 __invert__
函数中创建反向 class,并 return 新 class.
的实例
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
一种更冗长但非常明确的方法是使用两个辅助函数,并通过标志 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
另一种方式:
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
我在 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
如果我们按预期显式调用进入和退出函数,我们将得到:
with ReversibleContextManager() as c:
c.__enter__()
print('o')
c.__exit__()
d
d
o
g
g
但是实例方法的顺序是相反的!
with ~ReversibleContextManager() as c:
c.__enter__()
print('o')
c.__exit__()
d
g
o
d
g
所以看起来 with 语句调用使用绑定到 Class 的方法而不是实例(这是正确的术语吗?)。这是预期的吗?
即
所谓的是:
c = ReversibleContextManager()
c.__invert__()
ReversibleContextManager.__enter__(c)
...in context...
ReversibleContextManager.__exit__(c)
而不是我所期望的:
c = ReversibleContextManager()
c.__invert__()
c.__enter__()
...in context...
c.__exit__()
So it looks like the with statement calls using the method bound to the Class rather than the instance (is this the right terminology?). Is this expected?
当然可以。这就是 Python 通常查找特殊方法的方式。如果你有一个 class Foo
实现了 __str__
,print Foo
调用 type(Foo).__str__
而不是 Foo.__str__
。
作为解决方法,您可以在 __invert__
函数中创建反向 class,并 return 新 class.
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
一种更冗长但非常明确的方法是使用两个辅助函数,并通过标志 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
另一种方式:
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