解释Python's '和& # 39;__exit__& # 39;

我在某人的代码中看到了这个。这是什么意思?

    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
410282 次浏览

使用这些神奇的方法(__enter____exit__)允许您实现可以通过with语句轻松使用的对象。

其思想是,它使构建需要执行一些“清理”代码的代码变得容易(可以将其视为try-finally块)。# EYZ1。

一个有用的例子是数据库连接对象(一旦对应的'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 -“with”语句也有不错的评价。

如果你知道环境经理是什么,那么你就不需要再理解__enter____exit__的魔法方法了。让我们看一个非常简单的例子。

在这个例子中,我在开放函数的帮助下打开myfile.txt文件。try / finally块确保即使发生意外异常,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)

如果你看一下代码,我没有关闭文件&没有try / finally区块。因为语句会自动关闭myfile.txt。您甚至可以通过调用print(fp.closed)属性来检查它——它返回True

这是因为开放函数返回的文件对象(在我的例子中是fp)有两个内置方法__enter____exit__。它也被称为上下文管理器。__enter__方法在块开始时被调用,__exit__方法在__exit__块结束时被调用。

注意:语句只适用于支持上下文管理协议的对象(即它们有__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文档非常困难,所以这里有链接来帮助其他人:

< p > # EYZ0
# EYZ0
(两个版本的细节是相同的)

< p > # EYZ0
输入与此对象相关的运行时上下文。with语句将该方法的返回值绑定到语句as子句中指定的目标(如果有的话)
< p > # EYZ0
退出与此对象相关的运行时上下文。参数描述了导致退出上下文的异常。如果没有异常退出上下文,所有三个参数都将是None.

如果提供了一个异常,而该方法希望抑制该异常(即防止它被传播),它应该返回一个真值。否则,异常将在退出此方法时正常处理。

注意,__exit__()方法不应该重新引发传入的异常;这是调用者的责任。

我希望清楚地描述__exit__方法参数。这是缺乏的,但我们可以推断他们…

假设exc_type是异常的类。

它说你不应该重新引发传入异常。这提示我们,其中一个参数可能是一个实际的异常实例…或者你应该自己从类型和值实例化它?

我们可以通过看这篇文章来回答:
# EYZ0 < / p >

例如,下面的__exit__方法接受任何TypeError,但允许所有其他异常通过:

def __exit__(self, type, value, traceback):
return isinstance(value, TypeError)

...所以很明显value是一个异常实例。

假设traceback是一个Python的回溯对象。

注意这里还有其他文档 # EYZ0 < / p >

...他们对__enter____exit__方法有更详细的解释。特别是,__exit__应该返回一个布尔值(尽管true或falsy也可以,例如,隐式的None返回将给出传播异常的默认行为)。

除了上面的回答来举例调用顺序外,还有一个简单的运行示例

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 my_handler时,变量my_handler将获得由__enter__()返回的值,在上面的例子中是None!对于这种用途,需要定义返回值,例如:

def __enter__(self):
print('__enter__')
return self

试着加上我的答案(我的学习思路):

__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

服务器端的结果与之前相同

抱歉我的英语不好,解释不清,谢谢....

这被称为上下文管理器,我只是想补充一点,其他编程语言也存在类似的方法。比较它们有助于理解python中的上下文管理器。 基本上,当我们处理一些资源(文件、网络、数据库)时,上下文管理器会被使用,这些资源需要初始化,并且在某些时候需要销毁(销毁)。在Java 7和上面,我们有自动资源管理,其形式为:

//Java code
try (Session session = new Session())
{
// do stuff
}

注意,Session需要实现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语句来实现相同的机制。这只是语法糖。

当执行进入with语句的上下文,并且是时候获取资源时,Python调用__enter__。当执行再次离开上下文时,Python调用__exit__来释放资源

让我们考虑一下上下文管理器和Python中的“with”语句。Context Manager是一个简单的“协议”(或接口),您的对象需要遵循该协议,以便与with语句一起使用。基本上,如果你想让对象发挥上下文管理器的作用,你所需要做的就是向对象添加输入退出方法。Python将在资源管理周期的适当时间调用这两个方法。

让我们看看实际情况是怎样的。下面是open()上下文管理器的简单实现:

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()

我们的ManagedFile类遵循上下文管理器协议,现在支持with语句。

>>> with ManagedFile('hello.txt') as f:
...    f.write('hello, world!')
...    f.write('bye now')`enter code here`

当执行进入with语句的上下文,并且是时候获取资源时,Python调用输入。当执行再次离开上下文时,Python调用退出来释放资源。

在Python中,编写基于类的上下文管理器并不是支持with语句的唯一方法。标准库中的contextlib实用程序模块提供了更多构建在基本上下文管理器协议之上的抽象。如果您的用例与contextlib提供的相匹配,这将使您的工作更加轻松。