Pythons 'with'-语句:正确 nest/derive 类 和 __enter__/__exit__
Pythons 'with'-statement: correctly nest/derive classes with __enter__/__exit__
我应该如何正确嵌套 类 的 with
相关行为(例如,在派生或实例化时)?
这对我有用,但我想知道是否有专门的方法来做到这一点:
class class_a:
def __init__(self):
print('class_a::__init__')
def __enter__(self):
print('class_a::__enter__')
return self
def __exit__(self, type, exit, tb):
print('class_a::__exit__')
class class_b(class_a):
def __init__(self):
class_a.__init__(self)
print('class_b::__init__')
def __enter__(self):
class_a.__enter__(self)
print('class_b::__enter__')
return self
def __exit__(self, type, exit, tb):
class_a.__exit__(self, type, exit, tb)
print('class_b::__exit__', type, exit, tb)
with class_b():
print('ready')
try:
signal.pause()
except:
pass
一种不同的方法是像这样实现 class_b
:
class class_b:
def __init__(self):
self._class_a_inst = class_a()
print('class_b::__init__')
def __enter__(self):
self._class_a_inst.__enter__()
print('class_b::__enter__')
return self
def __exit__(self, type, exit, tb):
self._class_a_inst.__exit__(type, exit, tb)
print('class_b::__exit__', type, exit, tb)
__enter__()
/ __exit__()
行为有什么区别吗?
理想情况下,使用 contextlib.contextmanager
。对于求导的情况:
import contextlib
class context_mixin:
def __enter__(self):
self.__context = self.context()
return self.__context.__enter__()
def __exit__(self, *args):
return self.__context.__exit__(*args)
class class_a(context_mixin):
@contextlib.contextmanager
def context(self):
print('class_a enter')
try:
yield self
finally:
print('class_a exit')
class class_b(class_a):
@contextlib.contextmanager
def context(self):
with super().context():
print('class_b enter')
try:
yield self
finally:
print('class_b exit')
在Python2中,super()
需要为super(class_b, self)
。
与您的代码相比,行为发生了变化:此代码在退出 a
之前退出 b
,这意味着范围嵌套。您已经编写代码以按其他顺序执行它们,尽管这很容易更改。通常它没有什么区别,但当它确实重要时,你通常希望事情嵌套。因此,对于一个(公认的)示例,如果 class_a
表示一个打开的文件,而 class_b
表示某种文件格式,那么 class_a
的退出路径将关闭文件,而退出class_b
的路径将写入任何尚未提交的缓冲更改。显然 b
应该先发生!
对于持有另一个对象的情况:
class class_b(context_mixin):
def __init__(self):
self.a = class_a()
@contextlib.contextmanager
def context(self):
with self.a:
print('class_b enter')
try:
yield self
finally:
print('class_b exit')
我应该如何正确嵌套 类 的 with
相关行为(例如,在派生或实例化时)?
这对我有用,但我想知道是否有专门的方法来做到这一点:
class class_a:
def __init__(self):
print('class_a::__init__')
def __enter__(self):
print('class_a::__enter__')
return self
def __exit__(self, type, exit, tb):
print('class_a::__exit__')
class class_b(class_a):
def __init__(self):
class_a.__init__(self)
print('class_b::__init__')
def __enter__(self):
class_a.__enter__(self)
print('class_b::__enter__')
return self
def __exit__(self, type, exit, tb):
class_a.__exit__(self, type, exit, tb)
print('class_b::__exit__', type, exit, tb)
with class_b():
print('ready')
try:
signal.pause()
except:
pass
一种不同的方法是像这样实现 class_b
:
class class_b:
def __init__(self):
self._class_a_inst = class_a()
print('class_b::__init__')
def __enter__(self):
self._class_a_inst.__enter__()
print('class_b::__enter__')
return self
def __exit__(self, type, exit, tb):
self._class_a_inst.__exit__(type, exit, tb)
print('class_b::__exit__', type, exit, tb)
__enter__()
/ __exit__()
行为有什么区别吗?
理想情况下,使用 contextlib.contextmanager
。对于求导的情况:
import contextlib
class context_mixin:
def __enter__(self):
self.__context = self.context()
return self.__context.__enter__()
def __exit__(self, *args):
return self.__context.__exit__(*args)
class class_a(context_mixin):
@contextlib.contextmanager
def context(self):
print('class_a enter')
try:
yield self
finally:
print('class_a exit')
class class_b(class_a):
@contextlib.contextmanager
def context(self):
with super().context():
print('class_b enter')
try:
yield self
finally:
print('class_b exit')
在Python2中,super()
需要为super(class_b, self)
。
与您的代码相比,行为发生了变化:此代码在退出 a
之前退出 b
,这意味着范围嵌套。您已经编写代码以按其他顺序执行它们,尽管这很容易更改。通常它没有什么区别,但当它确实重要时,你通常希望事情嵌套。因此,对于一个(公认的)示例,如果 class_a
表示一个打开的文件,而 class_b
表示某种文件格式,那么 class_a
的退出路径将关闭文件,而退出class_b
的路径将写入任何尚未提交的缓冲更改。显然 b
应该先发生!
对于持有另一个对象的情况:
class class_b(context_mixin):
def __init__(self):
self.a = class_a()
@contextlib.contextmanager
def context(self):
with self.a:
print('class_b enter')
try:
yield self
finally:
print('class_b exit')