with open("demo.txt", "r", encoding="utf-8") as f: pass # écrire un manager class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) def __enter__(self): return self.file_obj def __exit__(self, type, value, traceback): self.file_obj.close() # Just by defining __enter__ and __exit__ methods we can use our new class in a with statement. Let’s try: # # with File('demo.txt', 'w') as opened_file: # opened_file.write('Hola!') # # Our __exit__ method accepts three arguments. They are required by every __exit__ method which is a part of a Context Manager class. Let’s talk about what happens under-the-hood. # # The with statement stores the __exit__ method of the File class. # It calls the __enter__ method of the File class. # The __enter__ method opens the file and returns it. # The opened file handle is passed to opened_file. # We write to the file using .write(). # The with statement calls the stored __exit__ method. # The __exit__ method closes the file. # We did not talk about the type, value and traceback arguments of the __exit__ method. Between the 4th and 6th step, if an exception occurs, Python passes the type, value and traceback of the exception to the __exit__ method. It allows the __exit__ method to decide how to close the file and if any further steps are required. In our case we are not paying any attention to them. # # What if our file object raises an exception? We might be trying to access a method on the file object which it does not supports. For instance: # # with File('demo.txt', 'w') as opened_file: # opened_file.undefined_function('Hola!') # # Let’s list the steps which are taken by the with statement when an error is encountered: # # It passes the type, value and traceback of the error to the __exit__ method. # It allows the __exit__ method to handle the exception. # If __exit__ returns True then the exception was gracefully handled. # If anything other than True is returned by the __exit__ method then the exception is raised by the with statement.