本文翻译自:Explaining Python's '__enter__' and '__exit__'
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
#1楼
参考:https://stackoom.com/question/8KDF/解释Python的-enter-和-exit
#2楼
Using these magic methods ( __enter__
, __exit__
) allows you to implement objects which can be used easily with the with
statement. 使用这些魔术方法( __enter__
, __exit__
)可以实现可以使用with
语句轻松使用的对象。
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). 我们的想法是,它可以轻松构建需要执行一些“清理”代码的代码(将其视为try-finally
块)。 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): 一个有用的例子可能是数据库连接对象(一旦相应的'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()
...
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
语句一起with
(如果您使用的是Python 2.5,则可能需要在文件顶部使用from __future__ import with_statement
)。
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 -- The 'with' statement' has a nice writeup as well. PEP343 - 'with'语句'也有一个很好的写法。
#3楼
If you know what context managers are then you need nothing more to understand __enter__
and __exit__
magic methods. 如果您知道上下文管理器是什么,那么您无需了解__enter__
和__exit__
魔术方法。 Lets see a very simple example. 让我们看一个非常简单的例子。
In this example I am opening myfile.txt with help of open function. 在这个例子中,我在open函数的帮助下打开 myfile.txt 。 The try/finally block ensures that even if an unexpected exception occurs myfile.txt will be closed. try / finally块确保即使发生意外异常, myfile.txt也将被关闭。
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语句打开同一个文件:
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. 如果你看代码,我没有关闭文件,没有try / finally块。 Because with statement automatically closes myfile.txt . 因为with语句会自动关闭myfile.txt 。 You can even check it by calling print(fp.closed)
attribute -- which returns True
. 你甚至可以通过调用print(fp.closed)
属性来检查它 - 它返回True
。
This is because the file objects (fp in my example) returned by open function has two built-in methods __enter__
and __exit__
. 这是因为open函数返回的文件对象(在我的例子中为fp)有两个内置方法__enter__
和__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. __enter__
方法在with的开头调用,最后调用__exit__
方法。 Note: with statement only works with objects that support the context mamangement protocol ie they have __enter__
and __exit__
methods. 注意: with语句仅适用于支持上下文管理协议的对象,即它们具有__enter__
和__exit__
方法。 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. 我希望你现在对__enter__
和__exit__
魔术方法有基本的了解。
#4楼
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: 我发现通过Googling找到__enter__
和__exit__
方法的python文档很奇怪,所以在这里帮助其他人是链接:
https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/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.with
语句将此方法的返回值绑定到语句的as子句中指定的目标(如果有)。
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
. 如果在没有异常的情况下退出上下文,则所有三个参数都将为None
。If an exception is supplied, and the method wishes to suppress the exception (ie, 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; 请注意,__exit__()
方法不应该重新传入传入的异常; this is the caller's responsibility. 这是来电者的责任。
I was hoping for a clear description of the __exit__
method arguments. 我希望明确描述__exit__
方法参数。 This is lacking but we can deduce them... 这是缺乏的,但我们可以推断它们......
Presumably exc_type
is the class of the exception. 推测exc_type
是异常的类。
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? 这告诉我们其中一个参数可能是一个实际的Exception实例......或者你应该从类型和值中自己实例化它?
We can answer by looking at this article: 我们可以通过查看这篇文章来回答:
http://effbot.org/zone/python-with-statement.htm http://effbot.org/zone/python-with-statement.htm
For example, the following
__exit__
method swallows any TypeError, but lets all other exceptions through: 例如,以下__exit__
方法吞下任何TypeError,但允许所有其他异常通过:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
...so clearly value
is an Exception instance. ...很明显, value
是一个Exception实例。
And presumably traceback
is a Python traceback object. 并且可能是traceback
是一个Python 回溯对象。
#5楼
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
! 提醒:当使用with myclass() as mc
的语法with myclass() as mc
,变量mc获取__enter__()
返回的值,在上面的例子中为None
! For such use, need to define return value, such as: 对于这种用法,需要定义返回值,例如:
def __enter__(self):
print('__enter__')
return self
#6楼
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. __enter__
和[__exit__]
都是在进入和退出“ with语句 ”( PEP 343 )主体时调用的方法,两者的实现都称为上下文管理器。
the with statement is intend to hiding flow control of try finally clause and make the code inscrutable. with语句打算隐藏try finally子句的流控制并使代码难以理解。
the syntax of the with statement is : with语句的语法是:
with EXPR as VAR:
BLOCK
which translate to (as mention in PEP 343) : 转化为(如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.... 对不起我的英文不好和我不清楚的解释,谢谢....