本文发自 http://www.binss.me/blog/talk-about-the-with-usage-in-python/,转载请注明出处。

在阅读ioloop源码时看到以下语句:

with self._callback_lock:
     callbacks = self._callbacks
     self._callbacks = []
# _callback_lock是互斥锁,即threading.Lock()

额,对于加锁,我的第一反应是这样的:

lock = threading.Lock()
# 加锁
lock.acquire()
do something
# 解锁
lock.release()
 那如果do something出错了怎么办?使用try...except...finally呗:
lock = threading.Lock()
lock.acquire()
try:
     do something
except:
     print “error"
finally:
     lock.release()

然而python提供了更优雅的方法——with:

The ‘with‘ statement clarifies code that previously would use try...finally blocks to ensure that clean-up code is executed.

The ‘with‘ statement is a control-flow structure whose basic structure is:

with expression [as variable]:
    with-block

The expression is evaluated, and it should result in an object that supports the context management protocol (that is, has __enter__() and __exit__() methods).

The object’s __enter__() is called before with-block is executed and therefore can run set-up code. It also may return a value that is bound to the name variable, if given. (Note carefully that variable is not assigned the result of expression.)

After execution of the with-block is finished, the object’s __exit__() method is called, even if the block raised an exception, and can therefore run clean-up code.

就是说要使用with语句的表达式(对象)必须实现__enter__()__exit__(),然后__enter__()会在with-block执行之前调用,__exit__()会在with-block执行完毕后调用,无论with-block是否抛出异常。

然后看看lock.py

def __enter__(self):
    return self.acquire()
def __exit__(self, typ, value, tb):
    self.release()

好吧,果然如此,果然很方便很优雅。那么as variable又是干什么用的呢?

根据文档:

with open('/etc/passwd', 'r') as f:
    for line in f:
        print line

variable是用来接收__enter__()返回的对象的。在这里,f接受了file的__enter__()的返回对象,从而能够迭代输出每一行。

如果系统没有帮我们实现__enter__()__exit__(),我们可以自己实现:

class DoTest:
    def __init__(self):
        self.count = 100

    def __enter__(self):
        print "enter"
        return self.count

    def __exit__(self, typ, value, tb):
        print "exit"

a = DoTest()
with a as l:
    print "do something error"
    print l
    raise Exception

输出为:

enter
do something error
100
exit
Traceback (most recent call last):
  File "/Users/bin/Desktop/TestCode/test.py", line 28, in <module>
    raise Exception
Exception

参考文章 https://docs.python.org/2/whatsnew/2.6.html#pep-343-the-with-statement