解释Python的‘__enter __‘和‘__exit__‘

本文翻译自: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. 输入与此对象相关的运行时上下文。 The with 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 be None . 如果在没有异常的情况下退出上下文,则所有三个参数都将为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.... 对不起我的英文不好和我不清楚的解释,谢谢....

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值