Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python __enter__ / __exit__ vs __init__ (or __new__) / __del__

I have searched and I'm unable to come up with any good reason to use python's __enter__ /__exit__ rather than __init__ (or __new__ ?) / __del__ .

I understand that __enter__ / __exit__ are intended for use with the with statement as context managers, and the with statement is great. But the counterpart to that is that any code in those blocks is only executed in that context. By using these instead of __init__ / __del__ I appear to be creating an implicit contract with callers that they must use with, yet there's no way to enforce such a contract, and the contract is only communicated via documentation (or reading the code). That seems like a bad idea.

I seem to get the same effect using __init__ / __del__ inside of a with block. But by using them rather than the context management methods my object is also useful in other scenarios.

So can anybody come up with a compelling reason why I would ever want to use the context management methods rather than the constructor/destructor methods?

If there's a better place to ask a question like this, please let me know, but it seems like there's not much good information about this out there.

Follow Up:

This question was based on a bad (but likely common) assumption because I always used with to instantiate a new object, in which case __init__/__del__ come very close to the same behavior as __enter__/__exit__ (except that you can't control when or if __del__ will be executed, it's up to garbage collection and if the process is terminated first it may never be called). But if you use pre-existing objects in with statements they are of course quite different.

like image 791
BobDoolittle Avatar asked Nov 10 '16 20:11

BobDoolittle


People also ask

What does __ exit __ do in Python?

__exit__ in Python Context manager is used for managing resources used by the program. After completion of usage, we have to release memory and terminate connections between files.

What does __ enter __ do in Python?

__enter__ and [__exit__] both are methods that are invoked on entry to and exit from the body of "the with statement" (PEP 343) and implementation of both is called context manager. the with statement is intend to hiding flow control of try finally clause and make the code inscrutable.

How do you exit a class in Python?

Python exit commands: quit(), exit(), sys. exit() and os. _exit() The functions quit() , exit() , sys.

What is ExitStack?

An ExitStack is (as the name suggests) a stack of clean-up functions. Adding a callback to the stack is the equivalent of calling Go's defer statement. However, clean-up functions are not executed when the function returns, but when execution leaves the with block - and until then, the stack can also be emptied again.


2 Answers

There are several differences you appear to have missed:

  • Context manager get a chance to provide a new object just for the block you are executing. Some context managers just return self there (like file objects do), but, as an example, database connection objects could return a cursor object tied to the current transaction.

  • Context managers are not just notified of the context ending, but also if the exit was caused by an exception. It can then decide on handling that event or otherwise react differently during exit. Using a database connection as an example again, based on there being an exception you could either commit or abort the transaction.

  • __del__ is only called when all references to an object are removed. This means you can't rely on it being called if you need to have multiple references to it that you may or may not control the lifetime of. A context manager exit is precisely defined however.

  • Context managers can be reused, and they can keep state. The database connection again; you create it once, then use it as a context manager again and again, and it'll keep that connection open. There is no need to create a new object each time for this.

    This is important for thread locks, for example; you have to keep state so that only one thread can hold the lock at a time. You do this by creating one lock object, then use with lock: so different threads executing that section each can be made to wait before entering that context.

The __enter__ and __exit__ methods form the context manager protocol, and you should only use these if you actually want to manage a context. The goal of context managers is to simplify common try...finally and try...except patterns, not to manage the lifetime of a single instance. See PEP 343 – The "with" Statement:

This PEP adds a new statement "with" to the Python language to make it possible to factor out standard uses of try/finally statements.

like image 133
Martijn Pieters Avatar answered Sep 17 '22 17:09

Martijn Pieters


del x doesn’t directly call x.__del__()

You have no control over when .__del__ is called, or in fact whether it gets called at all.

Therefore, using __init__/__del__ for context management is not reliable.

like image 30
wim Avatar answered Sep 16 '22 17:09

wim