问题:解释Python的’__enter__’和’__exit__’
我在某人的代码中看到了这一点。这是什么意思?
def __enter__(self):
return self
def __exit__(self, type, value, tb):
self.stream.close()
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
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()
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
回答 0
使用这些魔术方法(__enter__
,__exit__
)使您可以实现可轻松用于该with
语句的对象。
这个想法是,它使得构建需要执行一些“清除”代码的代码(将其视为一个try-finally
块)变得容易。这里有更多的解释。
一个有用的例子是数据库连接对象(一旦对应的“ with”语句超出范围,它就会自动关闭连接):
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()
...
如上所述,将此对象与with
语句一起使用(from __future__ import with_statement
如果您使用的是Python 2.5,则可能需要在文件顶部执行此操作)。
with DatabaseConnection() as mydbconn:
# do stuff
PEP343-‘with’语句’也有不错的写法。
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.
回答 1
如果您知道上下文管理器是什么,那么您就不需要了解__enter__
和使用__exit__
魔术方法。让我们看一个非常简单的例子。
在此示例中,我将借助open函数打开myfile.txt。在尝试/终于块确保即使发生意外的异常myfile.txt的将被关闭。
fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
for line in fp:
print(line)
finally:
fp.close()
现在我用with语句打开相同的文件:
with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
for line in fp:
print(line)
如果您查看代码,则我没有关闭文件,也没有try / finally块。因为with语句会自动关闭 myfile.txt。您甚至可以通过调用print(fp.closed)
attribute 来检查它-返回True
。
这是因为open函数返回的文件对象(在我的示例中为fp)具有两个内置方法__enter__
和__exit__
。它也称为上下文管理器。__enter__
方法在with块的开头__exit__
被调用,方法在结尾被调用。注意:with语句仅适用于支持上下文管理协议的对象,即它们具有__enter__
和__exit__
方法。实现这两种方法的类称为上下文管理器类。
现在让我们定义自己的上下文管理器类。
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")
我希望您现在对魔术方法__enter__
和两者都有基本的了解__exit__
。
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 myfile.txt 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 mamangement 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.
回答 2
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. The with
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 be None
.
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.
回答 3
除了上述答案以例证调用顺序外,一个简单的运行示例
class myclass:
def __init__(self):
print("__init__")
def __enter__(self):
print("__enter__")
def __exit__(self, type, value, traceback):
print("__exit__")
def __del__(self):
print("__del__")
with myclass():
print("body")
产生输出:
__init__
__enter__
body
__exit__
__del__
提醒:使用语法时,在上述情况下with myclass() as mc
,变量mc获取由返回的值!对于此类用途,需要定义返回值,例如:__enter__()
None
def __enter__(self):
print('__enter__')
return self
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, type, value, traceback):
print("__exit__")
def __del__(self):
print("__del__")
with myclass():
print("body")
Produces the output:
__init__
__enter__
body
__exit__
__del__
A reminder: when using the syntax with myclass() as mc
, variable mc 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
回答 4
尝试添加我的答案(我的学习思想):
__enter__
[__exit__]
两者都是在“ with语句 ” 主体(PEP 343)的入口处和出口处被调用的方法,并且两者的实现都称为上下文管理器。
with语句旨在隐藏try finally子句的流控制,并使代码难以理解。
with语句的语法为:
with EXPR as VAR:
BLOCK
转换为(如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)
尝试一些代码:
>>> 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'
然后手动尝试(遵循翻译语法):
>>> 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
服务器端的结果与以前相同
对不起,我的英语不好,解释不清楚,谢谢。
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….
回答 5
这称为上下文管理器,我只想补充一下,其他编程语言也存在类似的方法。比较它们可能有助于理解python中的上下文管理器。基本上,当我们处理一些需要初始化的资源(文件,网络,数据库),并且在某些时候需要拆除(配置)时,使用上下文管理器。在Java 7及更高版本中,我们具有以下形式的自动资源管理:
//Java code
try (Session session = new Session())
{
// do stuff
}
请注意,会话需要实现AutoClosable
其(许多)子接口之一。
在C#中,我们使用using语句来管理资源,其形式为:
//C# code
using(Session session = new Session())
{
... do stuff.
}
Session
应该在其中实施IDisposable
。
在python中,我们使用的类应实现__enter__
和__exit__
。因此它采取以下形式:
#Python code
with Session() as session:
#do stuff
正如其他人指出的那样,您始终可以在所有语言中使用try / finally语句来实现相同的机制。这只是语法糖。
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.