python上下文管理,with语句
今天在网上看到一段代码,其中使用了with seam:初见不解其意,遂查询资料.
代码:
1 #! /usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import time 4 from random import random 5 from threading import Thread,Semaphore 6 7 sema = Semaphore(3) 8 9 def foo(tid): 10 with sema: 11 print("{} acquire sema".format(tid)) 12 wt = random() * 2 13 time.sleep(wt) 14 print("{} release sema".format(tid)) 15 16 threads = [] 17 18 for i in range(5): 19 t = Thread(target=foo,args=(i,)) 20 threads.append(t) 21 t.start() 22 23 for t in threads: 24 t.join()
查询 python核心编程第二版,其中有个章节,专门介绍了with语句和上下文管理.
with语句,在Python2.6正式启用,和try-except-finally 类似,with语句也是用来简化代码的.Python中,并非任意符号都可以使用with语句,with语句仅仅工作于支持上下文管理协议的对象.
with应用场景:打开文件,日志,数据库,线程资源,简单同步,数据库连接等等.
1 with context_expr [as var]: ##context_expr:上下文表达式,[]可选 2 with_suite
能和with一起使用的成员列表:
1 file 2 decimal.Context 3 thread.LockType 4 threading.Lock 5 threading.RLock 6 threading.Condition 7 threading.Semaphore 8 threading.BoundedSemaphore
最最常见的with用法:
1 with open("file","r") as f:
for line in f: 2 pass
上面的代码都做了什么操作?
程序试图打开一个文本,如果一切正常,把文本对象赋值给f,然后用迭代器遍历文本中的每一行.当完成时,关闭文本.
无论在这段代码的开始,中间,还是结束时发生了异常,都会执行清理的代码,此外文件仍然被会自动的关闭.
先上一个最简单的代码:
1 class Context: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 print("Begin.__enter__") 6 return self 7 def __exit__(self, exc_type, exc_val, exc_tb): 8 print("End.__exit__") 9 def context(self): 10 print("This is context ...{}".format(self.name)) 11 12 with Context("xurui") as context: ##如果带上 as 变量,那么__enter__()方法必须得返回一个东西,要不然会报错.. 13 context.context() 14 with Context("xurui"): 15 Context("xurui").context()
演示代码:
class Sample: """ ##执行__enter__方法,它将完成with语句块执行前的所有准备工作,如果with xx 后面带上参数,as val,那么__enter__返回值将赋值给 val,否则,丢弃返回值""" def __enter__(self): print("In __enter__()") return "Foo" ##返回"Foo"给as Sample中的Sample def __exit__(self, exc_type, exc_val, exc_tb): print("In __exit__()") def get_sample(): print(type(Sample())) return Sample() with get_sample() as Sample: print("sample:{}".format(Sample)) 结果: In __enter__() sample:Foo In __exit__()
但这都不是with的牛逼功能,with最强的地方,还是用来处理异常...
__exit__(),有三个参数,类型(异常类),值(异常实例),和回溯(回溯对象).
演示代码:
class Sample: """ ##执行__enter__方法,它将完成with语句块执行前的所有准备工作,如果with xx 后面带上参数,as val,那么__enter__返回值将赋值给 val,否则,丢弃返回值""" def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): print("type:{},\nvalue:{},\ntrace:{}".format(exc_type,exc_val,exc_tb)) def do_something(self): bar = 1 / 0 return bar + 10 with Sample() as Sample: Sample.do_something() print("sample:{}".format(Sample)) 结果: type:<class 'ZeroDivisionError'>, value:division by zero, trace:<traceback object at 0x0000000001074CC8> Traceback (most recent call last): File "C:/Users/xurui/PycharmProjects/q1/2017-03-01/error.py", line 55, in <module> Sample.do_something() File "C:/Users/xurui/PycharmProjects/q1/2017-03-01/error.py", line 51, in do_something bar = 1 / 0 ZeroDivisionError: division by zero
1 import queue 2 import contextlib 3 4 5 @contextlib.contextmanager 6 def worker_state(xxx, val): 7 xxx.append(val) 8 # print(xxx) 9 try: 10 yield 11 finally: 12 xxx.remove(val) 13 # print(xxx) 14 15 16 q = queue.Queue() 17 li = [] 18 q.put("xurui") 19 with worker_state(li, 1): 20 print("before", li) 21 q.get() 22 print("after", li)
自定义一个open文件
1 import contextlib 2 3 4 @contextlib.contextmanager 5 def MyOpen(filename, mode): 6 try: 7 f = open(filename, mode, encoding='utf') 8 except Exception as e: 9 pass 10 else: 11 yield f ##f return 给 as f的f 12 finally: 13 f.close() 14 15 16 with MyOpen("1.py", 'r') as f: 17 ret = f.readlines() 18 print(ret)
人生苦短,我用python!