解释 Python 的 '__enter__' 和 '__exit__'

2025-02-08 08:52:00
admin
原创
43
摘要:问题描述:我在某人的代码中看到了这个。这是什么意思? def __enter__(self): return self def __exit__(self, type, value, tb): self.stream.close() 这是完整的代码。from _...

问题描述:

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

    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:

使用这些魔术方法(__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语句一起使用(如果您使用的是 Python 2.5,则可能需要from __future__ import with_statement在文件顶部执行此操作)。

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343——“with”语句也有很好的说明。

解决方案 2:

如果你知道什么是上下文管理器,那么你就不需要再了解__enter__魔法__exit__方法了。让我们看一个非常简单的例子。

在此示例中,我借助open函数打开了myfile.txt文件。try /finally块确保即使发生意外异常,myfile.txt也会被关闭。

fp=open(r"C:UsersSharpElDesktopmyfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

现在我使用with语句打开同一个文件:

with open(r"C:UsersSharpElDesktopmyfile.txt") as fp:
    for line in fp:
        print(line) 

如果您查看代码,您会发现我没有关闭文件,并且没有try/finally块。因为with语句会自动关闭 myfile.txt。您甚至可以通过调用print(fp.closed)返回的 attribute来检查它True

这是因为open函数返回的文件对象(在我的示例中为 fp)具有两个内置方法__enter____exit__。它也被称为上下文管理器。方法在with__enter__块的开头调用, 方法在结尾调用。__exit__

注意:with语句仅适用于支持上下文管理协议的对象(即它们具有__enter____exit__方法)。实现这两种方法的类称为上下文管理器类。

现在让我们定义自己的上下文管理器类。

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'
')
    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:UsersSharpElDesktopmyfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

__enter__我希望现在您对这两种魔法方法都有了基本的了解__exit__

解决方案 3:

我发现通过 Google 搜索找到 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)

退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文退出时没有出现异常,则所有三个参数均为None

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

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

我希望对__exit__方法参数有一个清晰的描述。这还不够,但我们可以推断出来……

大概exc_type是该异常的类别。

它说你不应该重新引发传入的异常。这告诉我们其中一个参数可能是实际的异常实例……或者也许你应该自己根据类型和值来实例化它?

我们可以通过查看这篇文章来回答:

http ://effbot.org/zone/python-with-statement.htm

例如,以下__exit__方法会吞掉任何 TypeError,但让所有其他异常通过:

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

...显然value是一个异常实例。

并且大概traceback是一个 Python回溯对象。

请注意,这里还有更多文档:

https ://docs.python.org/3/library/stdtypes.html#context-manager-types

__enter__...他们对和方法的解释稍微详细一些__exit__。特别是,更明确地指出__exit__应该返回布尔值(尽管真或假都可以正常工作,例如隐式None返回将提供传播异常的默认行为)。

解决方案 4:

除了上述示例调用顺序的答案外,还有一个简单的运行示例

class MyClass:
    def __init__(self):
        print("__init__")

    def __enter__(self):
        print("__enter__")

    def __exit__(self, ex_type, ex_value, ex_traceback):
        print(f"ex_type, ex_value, ex_traceback={ex_type, ex_value, ex_traceback}")
        # if some errors occur, do special processing here
        if ex_type == KeyError:
            # raise the exception to be handled by upper calling functions
            return False
            # return True: ignore the exception

        # all other cases go to the default exit, e.g. do something to close resources, etc.
        print("__exit__")

    def __del__(self):
        print("__del__")

with MyClass():
    print("body")

    # # uncomment to simulate special Exceptions e.g. KeyError to process in __exit__()
    # raise KeyError("key error")

产生输出:

__init__
__enter__
body
ex_type, ex_value, ex_traceback=(None, None, None)
__exit__
__del__

注意:使用语法时with MyClass() as my_handler,变量 my_handler 获取 返回的值__enter__(),在上述情况下None!对于这样的使用,需要定义返回值,例如:

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

解决方案 5:

尝试添加我的答案(我的学习想法):

__enter__并且两者都是在进入和退出“ with 语句[__exit__]”主体(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 \n>>>         : %(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

服务器端的结果和之前一样

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

解决方案 6:

这称为上下文管理器,我只想补充一点,其他编程语言也存在类似的方法。比较它们可能有助于理解 Python 中的上下文管理器。基本上,当我们处理一些需要初始化并在某个时候拆除(处置)的资源(文件、网络、数据库)时,会使用上下文管理器。在Java 7及更高版本中,我们有自动资源管理,其形式如下:

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

请注意,Session 需要实现AutoClosable其多个子接口之一。

C#中,我们使用语句来管理资源,其形式如下:

//C# code
using(Session session = new Session())
{
  ... do stuff.
}

在其中Session应该实施IDisposable

python中,我们使用的类应该实现__enter____exit__。因此它采用以下形式:

#Python code
with Session() as session:
    #do stuff

正如其他人指出的那样,您始终可以在所有语言中使用 try/finally 语句来实现相同的机制。这只是语法糖。

解决方案 7:

__enter__当执行进入 with 语句的上下文并需要获取资源时,Python 会调用。当执行再次离开上下文时,Python 会调用__exit__以释放资源

让我们考虑一下 Python 中的上下文管理器和“with”语句。上下文管理器是一个简单的“协议”(或接口),您的对象需要遵循它,以便它可以与 with 语句一起使用。基本上,如果您希望对象充当上下文管理器,您需要做的就是向对象添加enterexit方法。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 会调用enter 。当执行再次离开上下文时,Python 会调用exit来释放资源。

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用