是否有一种Pythonic方法可以自动__exit__
一个班级的所有成员?
class C:
def __init__(self):
self.a = open('foo')
self.b = open('bar')
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
# Is it correct to just forward the parameters here?
self.a.__exit__(self, exc_type, exc_value, traceback)
self.b.__exit__(self, exc_type, exc_value, traceback)
我是否可以在__exit__
和a
上手动调用b
来执行此操作?我是否正确地呼叫__exit__
?
假设我所拥有的资源不像示例中那样file
,并且没有像close
或destroy
那样的方法。在__enter__
和__exit__
之上实现这样的方法可能是一种好习惯吗?
答案 0 :(得分:3)
正如我在评论中提到的那样:
我认为这里最有帮助的将是:
contextlib.ExitStack
您可以在__init__
中创建此对象作为您自己的类的成员。
然后使用enter_context(cm)
添加所有依赖上下文管理器,其中 cm 是context_manager
def __init__(self):
self.exit_stack = contextlib.ExitStack()
self.exit_stack.__enter__()
self.exit_stack.enter_context(open('foo'))
....
清除所有依赖上下文,只需调用此堆栈的__exit__
出口即可。
或者更好的只是子类ExitStack
并在init中调用enter_context
。
答案 1 :(得分:1)
编写自己的__enter__
和__exit__
函数往往不是一个好主意。您需要了解在子__exit__
期间发生异常时应该发生什么,或者如果__exit__
返回真实值,则意味着什么。
通常更好的方法是编写基于生成器的上下文管理器。例如
from contextlib import contextmanager
class A:
def __init__(self, filename0, filename1, file0, file1):
self.filename0 = filename0
self.filename1 = filename1
self.file0 = file0
self.file1 = file1
@classmethod
@contextmanager
def create(cls, filename0, filename1):
with open(filename0) as file0, \
open(filename1) as file1:
yield cls(filename0, filename1, file0, file1)
with A.create('file0.txt', 'file1.txt') as a:
a.do_something()
这将按定义的顺序打开子上下文管理器,并按照定义的顺序自动关闭它们,传播异常并正确返回值。
答案 2 :(得分:0)
__enter__
和__exit__
特殊功能几乎从不手动调用。在进入和离开with
块语句时分别调用。所以如果你使用类似的东西:
with C() as c:
# stuff
# other stuff
你称这些神奇的功能。在您的情况下,我会调用open
中的文件__enter__
和close
中相应的__exit__
函数
例如:
class C:
def __enter__(self):
self.a = open('foo')
self.b = open('bar')
return self
def __exit__(self, exc_type, exc_value, traceback):
self.a.close()
self.b.close()
def readline_a(self):
return self.a.readline()
def readline_b(self):
return self.b.readline()
with C() as c:
print(c.readline_a())
print(c.readline_b())
打印每个文件的第一行foo
和bar
答案 3 :(得分:0)
要为实例成员提供上下文管理器功能,可以执行以下操作:
class MemberManager:
managed_member_names = ('a', 'b', 'c')
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def __enter__(self):
# yield statement means this enter method returns a generator
for i in (getattr(self,n) for n self.managed_member_names):
with open(i, mode="w") as x:
# yield prevents the context manager from exiting
yield x
def __exit__(self, exc_type, exc_value, traceback):
# all items will be closed by __enter__ context manager; nothing needed here
pass
mm = MemberManager(fname1, fname2, fname3)
with mm as open_members:
# open_members is a generator/iterator
for member in open_members:
member.write("foo")
但请注意,您无法执行此操作:
with mm as open_members:
open_member_list = list(open_members)
open_member_list[0].write("foo") # ValueError: I/O operation on closed file.
open_members
迭代器必须保持无法使用,以使当前文件保持打开状态。