Is there a Pythonic way to automatically
__exit__ all members of a class?
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)
Can I do this without manually calling
b? Am I even calling
Suppose that the resources I have aren't
files like in the example and there isn't a method like
destroy. Is it perhaps good practice to implement a method like this on top of the
__exit__ special functions are almost never called manually. There are respectively called when entering and leaving a
with block statement. So if you use something like:
with C() as c: # stuff # other stuff
you call these magic functions. In you case, I would call the file
open functions in
__enter__ and the corresponding
close function in
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())
prints the first line of each file
So as I mentioned in comment :
I think most helpfull here will be :
You can create this object as a member of Your own class in the
And then add to it all your depending contextmanagers with the
enter_context(cm) where cm is context_manager
def __init__(self): self.exit_stack = contextlib.ExitStack() self.exit_stack.__enter__() self.exit_stack.enter_context(open('foo')) ....
to clear all the depending contexts just call in the
__exit__ the exit of this stack.
Or better just subclass
ExitStack and in init call the
Writing your own
__exit__ functions tends to not be a great idea. You need to understand what should happen if an exception occurs during a child
__exit__ or what it means if
__exit__ returns a truthful value.
It's generally better just to write a generator-based context manager instead. eg.
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()
This will open the child context managers in defined order, and automatically close them in a defined order, propagating exceptions and return values correctly.
To provide context manager functionality for instance members, one might do something like this:
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")
However, note that you cannot do this:
with mm as open_members: open_member_list = list(open_members) open_member_list.write("foo") # ValueError: I/O operation on closed file.
open_members iterator must remain unexhausted for the current file to remain open.