Reputation: 66797
I saw this in someone's code. What does it mean?
def __enter__(self):
return self
def __exit__(self, type, value, tb):
self.stream.close()
Here is the complete code.
from __future__ import with_statement#for python2.5
class a(object):
def __enter__(self):
print 'sss'
return 'sss111'
def __exit__(self ,type, value, traceback):
print 'ok'
return False
with a() as s:
print s
print s
Upvotes: 601
Views: 541841
Reputation: 2594
In addition to the above answers to exemplify invocation order, a simple run example
class MyClass:
def __init__(self):
print("__init__")
def __enter__(self):
print("__enter__")
def __exit__(self, ex_type, ex_value, ex_traceback):
print(f"ex_type, ex_value, ex_traceback={ex_type, ex_value, ex_traceback}")
# if some errors occur, do special processing here
if ex_type == KeyError:
# raise the exception to be handled by upper calling functions
return False
# return True: ignore the exception
# all other cases go to the default exit, e.g. do something to close resources, etc.
print("__exit__")
def __del__(self):
print("__del__")
with MyClass():
print("body")
# # uncomment to simulate special Exceptions e.g. KeyError to process in __exit__()
# raise KeyError("key error")
Produces the output:
__init__
__enter__
body
ex_type, ex_value, ex_traceback=(None, None, None)
__exit__
__del__
Note: when using the syntax with MyClass() as my_handler
, variable my_handler gets the value returned by __enter__()
, in the above case None
! For such use, need to define return value, such as:
def __enter__(self):
print('__enter__')
return self
Upvotes: 101
Reputation: 33923
I found it strangely difficult to locate the python docs for __enter__
and __exit__
methods by Googling, so to help others here is the link:
https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(detail is the same for both versions)
object.__enter__(self)
Enter the runtime context related to this object. Thewith
statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.
object.__exit__(self, exc_type, exc_value, traceback)
Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will beNone
.
If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
Note that
__exit__()
methods should not reraise the passed-in exception; this is the caller’s responsibility.
I was hoping for a clear description of the __exit__
method arguments. This is lacking but we can deduce them...
Presumably exc_type
is the class of the exception.
It says you should not re-raise the passed-in exception. This suggests to us that one of the arguments might be an actual Exception instance ...or maybe you're supposed to instantiate it yourself from the type and value?
We can answer by looking at this article:
http://effbot.org/zone/python-with-statement.htm
For example, the following
__exit__
method swallows any TypeError, but lets all other exceptions through:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
...so clearly value
is an Exception instance.
And presumably traceback
is a Python traceback object.
Note that there are further docs here:
https://docs.python.org/3/library/stdtypes.html#context-manager-types
...they have a slightly more detailed explanation of the __enter__
and __exit__
methods. Particularly it is more explicit that __exit__
should return a boolean value (though truthy or falsy will work fine, e.g. an implicit None
return will give the default behaviour of propagating the exception).
Upvotes: 109
Reputation: 416
try adding my answers (my thought of learning) :
__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.
the syntax of the with statement is :
with EXPR as VAR:
BLOCK
which translate to (as mention in PEP 343) :
mgr = (EXPR)
exit = type(mgr).__exit__ # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
try:
VAR = value # Only if "as VAR" is present
BLOCK
except:
# The exceptional case is handled here
exc = False
if not exit(mgr, *sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)
try some code:
>>> import logging
>>> import socket
>>> import sys
#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>> (clientsocket, addr) = s.accept()
>>> print('get connection from %r' % addr[0])
>>> msg = clientsocket.recv(1024)
>>> print('received %r' % msg)
>>> clientsocket.send(b'connected')
>>> continue
#the client side
>>> class MyConnectionManager:
>>> def __init__(self, sock, addrs):
>>> logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>> : %(levelname)s --> %(message)s')
>>> logging.info('Initiating My connection')
>>> self.sock = sock
>>> self.addrs = addrs
>>> def __enter__(self):
>>> try:
>>> self.sock.connect(addrs)
>>> logging.info('connection success')
>>> return self.sock
>>> except:
>>> logging.warning('Connection refused')
>>> raise
>>> def __exit__(self, type, value, tb):
>>> logging.info('CM suppress exception')
>>> return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>> try:
>>> CM.send(b'establishing connection')
>>> msg = CM.recv(1024)
>>> print(msg)
>>> except:
>>> raise
#will result (client side) :
2018-12-18 14:44:05,863 : INFO --> Initiating My connection
2018-12-18 14:44:05,863 : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864 : INFO --> CM suppress exception
#result of server side
get connection from '127.0.0.1'
received b'establishing connection'
and now try manually (following translate syntax):
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331 : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491 : INFO --> connection success
>>> exc = True
>>> try:
>>> try:
>>> VAR = value
>>> VAR.send(b'establishing connection')
>>> msg = VAR.recv(1024)
>>> print(msg)
>>> except:
>>> exc = False
>>> if not ext(*sys.exc_info()):
>>> raise
>>> finally:
>>> if exc:
>>> ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208 : INFO --> CM suppress exception
the result of the server side same as before
sorry for my bad english and my unclear explanations, thank you....
Upvotes: 8
Reputation: 116307
Using these magic methods (__enter__
, __exit__
) allows you to implement objects which can be used easily with the with
statement.
The idea is that it makes it easy to build code which needs some 'cleandown' code executed (think of it as a try-finally
block). Some more explanation here.
A useful example could be a database connection object (which then automagically closes the connection once the corresponding 'with'-statement goes out of scope):
class DatabaseConnection(object):
def __enter__(self):
# make a database connection and return it
...
return self.dbconn
def __exit__(self, exc_type, exc_val, exc_tb):
# make sure the dbconnection gets closed
self.dbconn.close()
...
As explained above, use this object with the with
statement (you may need to do from __future__ import with_statement
at the top of the file if you're on Python 2.5).
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 -- The 'with' statement' has a nice writeup as well.
Upvotes: 679
Reputation: 9531
If you know what context managers are then you need nothing more to understand __enter__
and __exit__
magic methods. Lets see a very simple example.
In this example I am opening the myfile.txt file with help of open function. The try/finally block ensures that even if an unexpected exception occurs myfile.txt will be closed.
fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
for line in fp:
print(line)
finally:
fp.close()
Now I am opening same file with with statement:
with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
for line in fp:
print(line)
If you look at the code, I didn't close the file & there is no try/finally block. Because with statement automatically closes myfile.txt . You can even check it by calling print(fp.closed)
attribute -- which returns True
.
This is because the file objects (fp in my example) returned by open function has two built-in methods __enter__
and __exit__
. It is also known as context manager. __enter__
method is called at the start of with block and __exit__
method is called at the end.
Note: with statement only works with objects that support the context management protocol (i.e. they have __enter__
and __exit__
methods). A class which implement both methods is known as context manager class.
Now lets define our own context manager class.
class Log:
def __init__(self,filename):
self.filename=filename
self.fp=None
def logging(self,text):
self.fp.write(text+'\n')
def __enter__(self):
print("__enter__")
self.fp=open(self.filename,"a+")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__")
self.fp.close()
with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
print("Main")
logfile.logging("Test1")
logfile.logging("Test2")
I hope now you have basic understanding of both __enter__
and __exit__
magic methods.
Upvotes: 146
Reputation: 71
Python calls __enter__
when execution enters the context of the with statement and it’s time to acquire the resource. When execution leaves the context again, Python calls __exit__
to free up the resource
Let's consider Context Managers and the “with” Statement in Python. Context Manager is a simple “protocol” (or interface) that your object needs to follow so it can be used with the with statement. Basically all you need to do is add enter and exit methods to an object if you want it to function as a context manager. Python will call these two methods at the appropriate times in the resource management cycle.
Let’s take a look at what this would look like in practical terms. Here’s how a simple implementation of the open() context manager might look like:
class ManagedFile:
def __init__(self, name):
self.name = name
def __enter__(self):
self.file = open(self.name, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
Our ManagedFile class follows the context manager protocol and now supports the with statement.
>>> with ManagedFile('hello.txt') as f:
... f.write('hello, world!')
... f.write('bye now')`enter code here`
Python calls enter when execution enters the context of the with statement and it’s time to acquire the resource. When execution leaves the context again, Python calls exit to free up the resource.
Writing a class-based context manager isn’t the only way to support the with statement in Python. The contextlib utility module in the standard library provides a few more abstractions built on top of the basic context manager protocol. This can make your life a little easier if your use cases matches what’s offered by contextlib.
Upvotes: 2
Reputation: 775
This is called context manager and I just want to add that similar approaches exist for other programming languages. Comparing them could be helpful in understanding the context manager in python. Basically, a context manager is used when we are dealing with some resources (file, network, database) that need to be initialized and at some point, tear downed (disposed). In Java 7 and above we have automatic resource management that takes the form of:
//Java code
try (Session session = new Session())
{
// do stuff
}
Note that Session needs to implement AutoClosable
or one of its (many) sub-interfaces.
In C#, we have using statements for managing resources that takes the form of:
//C# code
using(Session session = new Session())
{
... do stuff.
}
In which Session
should implement IDisposable
.
In python, the class that we use should implement __enter__
and __exit__
. So it takes the form of:
#Python code
with Session() as session:
#do stuff
And as others pointed out, you can always use try/finally statement in all the languages to implement the same mechanism. This is just syntactic sugar.
Upvotes: 7