解释Python的'__enter__'和'__exit__'
-
22-09-2019 - |
题
我在某人的代码中看到了这一点。这是什么意思?
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
解决方案
使用这些魔术方法(__enter__
,__exit__
),可以实现能够容易地与with
语句中使用的对象。
的想法是,它可以很容易地这就需要执行一些“cleandown”代码(认为它作为一个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
声明(你可能需要在文件的顶部,如果你对Python 2.5的做from __future__ import with_statement
)。
with DatabaseConnection() as mydbconn:
# do stuff
PEP343 - 在 '与' 语句'有一个很好的书面记录为好。
其他提示
如果你知道什么的上下文管理器是,那么你需要什么更多的了解__enter__
和__exit__
魔术方法。让我们看看一个非常简单的例子。
在这个例子中我开口的 myfile.txt的强>与打开强>功能的帮助。在的try /最后强>块确保即使发生了意外的异常的 myfile.txt的强>将被关闭。
fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
for line in fp:
print(line)
finally:
fp.close()
现在,我打开相同的文件是与语句:
with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
for line in fp:
print(line)
如果你在看代码,我没有关闭文件和不存在的尝试/终于阻止。因为<强>与强>语句自动关闭的 myfile.txt的即可。你甚至可以通过调用print(fp.closed)
属性检查 - 返回True
这是因为,通过打开强>函数返回的文件中的对象(在我的例子FP)具有两个内置方法__enter__
和__exit__
。它也被称为上下文管理。 __enter__
方法被称为在的与强>块和__exit__
方法被调用在端部开始。注:与语句只适用于支持上下文mamangement协议,即他们有__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__
魔术方法基本的了解。
我觉得奇怪的是很难找到由谷歌搜索__enter__
和__exit__
方法Python文档,所以帮助别人这里是链接:
https://docs.python.org/ 2 /参考/ datamodel.html#与语句上下文经理结果 https://docs.python.org/3/reference /datamodel.html#with-statement-context-managers 结果 (细节是相同的两个版本)
object.__enter__(self)
结果 输入与此相关的运行时环境。该with
语句将这个方法的返回值绑定到语句的作为子句中指定的目标(一个或多个),如果有的话。
object.__exit__(self, exc_type, exc_value, traceback)
结果 退出与此相关的运行时环境。参数描述导致上下文退出异常。如果上下文未经异常退出,所有三个参数将是None
。如果一个异常被提供,并且该方法希望抑制异常(即,防止它被传播)时,它应该返回一个真值。否则,该异常会被正常退出时从该方法加工。
请注意
__exit__()
方法不应该再加注传入的异常;这是调用者的责任。
我希望的的__exit__
方法参数的明确说明。这是缺乏,但我们可以推断出他们......
据推测exc_type
是类的异常。
它说,你不应该再提高传入的例外。这告诉我们,其中一个参数可能是一个实际的异常情况,或者......你应该从自己的类型和值实例呢?
我们可以通过看这篇文章回答:点击 http://effbot.org/zone/python-with-statement.htm
例如,下面的方法
__exit__
燕子任何类型错误,但通过让所有其他异常:
def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)
...如此明确value
是一个例外实例。
和推测traceback
是一个Python 回溯对象。
在除了上述答案举例调用顺序,一个简单的示例运行
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
尝试添加我的答案(我的学习想法):
__enter__
和 [__exit__]
两者都是在进入和退出“的主体时调用的方法”with 语句" (公众号 343)并且两者的实现称为上下文管理器。
with语句的目的是隐藏tryfinally子句的流程控制,使代码难以理解。
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
服务器端的结果和之前一样
抱歉我的英语不好和不清楚的解释,谢谢......