问题:解释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
回答 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’语句’也有不错的写法。
回答 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__
。
回答 2
我发现通过Googling 定位python文档__enter__
和__exit__
方法非常困难,因此,这里的链接可以帮助其他人:
https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(两个版本的细节相同)
object.__enter__(self)
输入与此对象相关的运行时上下文。该with
语句会将此方法的返回值绑定到该语句的as子句中指定的目标(如果有)。
object.__exit__(self, exc_type, exc_value, traceback)
退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文无exceptions地退出,则所有三个参数均为None
。如果提供了异常,并且该方法希望抑制该异常(即,防止其传播),则它应返回一个真值。否则,将在退出此方法后正常处理异常。
注意,
__exit__()
方法不应该引发传入的异常。这是调用方法的责任。
我希望对__exit__
方法参数进行清楚的描述。这是缺少的,但是我们可以推断出它们。
大概exc_type
是异常的类。
它说您不应该重新引发传入的异常。这向我们表明,其中一个参数可能是实际的Exception实例……或者您应该自己从类型和值中实例化它?
我们可以通过看这篇文章来回答:http :
//effbot.org/zone/python-with-statement.htm
例如,以下
__exit__
方法可吞噬所有TypeError,但允许所有其他异常通过:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
…显然value
是Exception实例。
大概traceback
是一个Python 回溯对象。
回答 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
回答 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
服务器端的结果与以前相同
对不起,我的英语不好,解释不清楚,谢谢。
回答 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语句来实现相同的机制。这只是语法糖。