with statement @Python
with语句
以最常见的文件操作为例,打开文件,必须有对应的关闭文件.
这样保证资源(文件句柄)不会泄漏.但是如何手动保证这一点,在复杂的程序中,就会是一个问题.
程序的逻辑可能比较复杂,而在有异常机制的语言中,控制流就更为复杂.
在C++中,类似的问题,我们有析构函数来保证这一步.在Python中,我们有with
语句.
with open(filename) as fp:
for line in fp.readlines():
pass
这样在with程序执行完毕,或者异常跳出的时候,都可以保证打开的文件必然会关闭.
上面的代码直观的不需要解释.(这就是Python的显著优点啊)
with
的语句格式如下:
with EXPR [as TARGET]:
BLOCK
深入一点点
with
语句其实是一种contextmanager
.它的语句格式就如上所示.
在执行with
语句是的执行顺序
- 执行表达式EXPR
- 注册contextmanager的__exit__方法
- 执行contextmanager的__enter__方法
- 如果有as,那么将EXPR的结果赋值给TARGET
- 执行后面BLOCK中的代码
- 执行__exit__方法
一个典型的示例程序contextMgr.py:
class myCtxMgr():
def __init__(self, val):
print "init"
self.val = val
def __enter__(self):
print "enter"
return self.val
# Don't use keyword type as var name
# as Guan XiQing's suggestion
def __exit__(self, typ, value, traceback):
print "leave"
return
with myCtxMgr(5) as val:
print val
__exit__
方法带的众多参数,就是为了保证在异常的情况下面,依然可以处理.
执行结果如下:
init
enter
5
leave
实例
with
语句看上去不错,但是有什么用处呢?
利用with语句可以简化我们的代码.特别是我们想进入一个上下文时候,做特定的操作.离开上下文的时候,做相应的逆操作.
上下文的英文就是context
最常见的锁操作,进来加锁,退出关锁.可惜Python中没有锁的概念,不能使用这个例子了.
在数据库的操作简单封装的时候,其实也有类似的需求.连接数据库,操作数据库,断开连接.这里的连接其实也是一种上下文.
下面放一个sqlite3的数据库操作的小例子 db.py
import sqlite3
import json
rootdir = "./"
db = rootdir + "db/gering.db"
table = "clst"
sqlRecent10 = "SELECT * FROM %s ORDER BY id DESC LIMIT 10;" % (table)
sqlInsertSQL = "INSERT INTO %s (clnum, json) VALUES (?,?);" % (table)
sqlMaxid = "SELECT * FROM %s ORDER BY id DESC LIMIT 1;" % (table)
sqlDelAll = "DELETE FROM %s;" % (table)
class openDB():
def __init__(self, db):
self.db = db
def __enter__(self):
self.conn = sqlite3.connect(self.db)
self.c = self.conn.cursor()
return self.conn, self.c
def __exit__(self, typ, value, trackback):
self.c.close()
def insertHist(clnum, jsonobj):
with openDB(db) as (conn, csr):
csr.execute(sqlInsertSQL, (clnum, json.dumps(jsonobj)))
newid = csr.lastrowid
conn.commit()
return newid
def getRecent10():
with openDB(db) as (_, csr):
csr.execute(sqlRecent10)
return csr.fetchall()
def getMaxID():
with openDB(db) as (_, csr):
csr.execute(sqlMaxid)
try:
ret = csr.fetchone()[0]
except TypeError:
ret = 0
return ret
def delAll():
with openDB(db) as (conn, csr):
csr.execute(sqlDelAll)
conn.commit()
return True
扩展
Python的标准库中有一些关于with
的扩展库contextlib
,用起来也比较有意思.
contextmanager
contextmanager是一种修饰器,配合yield
语句使用. tag.py
import contextlib
@contextlib.contextmanager
def htmlTag(tag):
print "<%s>" % (tag)
yield
print "</%s>" % (tag)
with htmlTag("h1"):
print "Header"
输出是什么,随手可以试一下.
利用contextmanager
,我们也可以快速的实现第一个小例子.这个就留给大家实践吧.
closing
closing
完全符合上面的关闭资源这一点的设计目的.首先closing假设其对象参数,含有close
方法.
当代码块BLOCK执行完毕的时候,自动调用对象参数的close
方法.
from contextlib import closing
import urllib
with closing(urllib.urlopen('http://www.google.com')) as page:
for line in page:
print line
ChangeLog
- rename
type
var name totyp