一、with的应用场景:
在python语言中,with语句适用于资源相关操作,锁相关操作,文件相关操作,异常处理等相关操作情景,可以代替try...except...finally...的模式,让程序员不用写一些可能会忘写的收尾的代码,使代码更简洁。
如下代码:在编码中可能会忘记关闭文件,或者在读取文件时发生异常,没有进行任何处理
import os
if not os.path.exists(u'F:/test'):
os.makedirs('F:/test')
f=open(u'F:/test/text.txt','r+')#打开文件,赋值给句柄
print f.read()#操作句柄读取数据
f.close()#关闭文件
如下代码:对可能出现的异常进行处理,finally关闭文件
import os
if not os.path.exists(u'F:/test'):
os.makedirs('F:/test')
try:
f=open(u'F:/test/text.txt','r+')#打开文件,赋值给句柄
print f.read()#操作句柄读取数据
except:
print 'fail to open'
exit(-1)
finally:
f.close()#关闭文件
为了代码简介,python用with来优雅的处理这段代码
import os
if not os.path.exists(u'F:/test'):
os.makedirs('F:/test')
with open(u'F:/test/text.txt','r+') as f:#打开文件,赋值给句柄
print f.read()#操作句柄读取数据
二、with语句原理
with expression [as target]:
with-body
expression是任意表达式,获取上下文管理器,as target可选,with-body是with语句的语句体
- 计算expression,并获取上下文管理器
- 保存上下文管理器的__exit()__方法
- 调用上下文管理器__enter()__方法
- 如果有as target,__enter()__方法的返回值赋给target
- 执行with语句体的内容,eg:上面打印出利用文件对象读取的文件内容
- 不管是否出现异常,调用第二步保存的上下文管理器的__exit()__方法,这里如果with语句体执行机过程中发生异常导致程序退出,那么异常的type、value、traceback等作为参数传递给__exit()__方法,否则,传递三个None
1、采用装饰器定义上下文管理器:
#coding:utf-8
from contextlib import contextmanager
@contextmanager
def demo():
print '__enter()__'
yield 'contextmangager demo'
print '__exit()__'
with demo() as demo:
print 'with-body: '+demo
__enter()__
with-body: contextmangager demo
__exit()__
2、采用自定义支持closing的对象:
#coding:utf-8
class closing(object):
def __init__(self,thing):
self.thing=thing
def __enter__(self):
print '__enter()__'
return self.thing
def __exit__(self,*exc_info):
print '__exit()__'
self.thing.close()
# 这里closing上下文管理器包装起来的对象必须具有close()方法的定义,否则会报错的
class demo():
def __init__(self):
self.thing=open('C:\Users\***\Desktop\111.txt','w')
def run(self):
print "function running"
def close(self):
self.thing.close()
with closing(demo()) as demo:
demo.run()
__enter()__
function running
__exit()__
3、采用类定义上下文管理器(廖雪峰大神教程的webapp里面就有许多这样的例子)
#coding:utf-8
k=0
m=0
class Sample:
def __init__(self):
global k
k+=1
print k
def __enter__(self):
global k
k+=1
print k
print "__enter__()"
global m
m+=1
return m
def __exit__(self, type, value, trace):
print "__exit__()"
with Sample() as sample:
print sample
1
2
__enter__()
1
__exit__()
这里强调一点:看了许多博客,有的博客里面指出as target操作,是将expression赋值给target,这是不对的,从上面例子可以看出,是将__enter__()方法返回值赋值给target。