上下文管理器的任务是:代码块执行前准备,代码块执行后收拾
1、如何使用上下文管理器:
如何打开一个文件,并写入"hello world"
1
2
3
4
5
|
filename
=
"my.txt"
mode
=
"w"
f
=
open
(filename,mode)
f.write(
"hello world"
)
f.close()
|
当发生异常时(如磁盘写满),就没有机会执行第5行。当然,我们可以采用try-finally语句块进行包装:
1
2
3
4
5
|
writer
=
open
(filename,mode)
try
:
writer.write(
"hello world"
)
finally
:
writer.close()
|
当我们进行复杂的操作时,try-finally语句就会变得丑陋,采用with语句重写:
1
2
|
with
open
(filename,mode) as writer:
writer.write(
"hello world"
)
|
as指代了从open()函数返回的内容,并把它赋给了新值。with完成了try-finally的任务。
2、自定义上下文管理器
with语句的作用类似于try-finally,提供一种上下文机制。要应用with语句的类,其内部必须提供两个内置函数__enter__和__exit__。前者在主体代码执行前执行,后者在主体代码执行后执行。as后面的变量,是在__enter__函数中返回的。
class echo():
def output(self):
print "hello world"
def __enter__(self):
print "enter"
return self #可以返回任何希望返回的东西
def __exit__(self,exception_type,value,trackback):
print "exit"
if exception_type==ValueError:
return True
else:
return Flase
>>>with echo as e:
e.output()
输出:
enter
hello world
exit
完备的__exit__函数如下:
def __exit__(self,exc_type,exc_value,exc_tb)
其中,exc_type:异常类型;exc_value:异常值;exc_tb:异常追踪信息
当__exit__返回True时,异常不传播
3、contextlib模块
contextlib模块的作用是提供更易用的上下文管理器,它是通过Generator实现的。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制,常用框架如下:
1 from contextlib import contextmanager 2 @contextmanager 3 def make_context(): 4 print 'enter' 5 try: 6 yield "ok" 7 except RuntimeError,err: 8 print 'error',err 9 finally: 10 print 'exit' 11 12 >>>with make_context() as value: 13 print value 14 15 输出为: 16 enter 17 ok 18 exit
其中,yield写入try-finally中是为了保证异常安全(能处理异常)as后的变量的值是由yield返回。yield前面的语句可看作代码块执行前操作,yield之后的操作可以看作在__exit__函数中的操作。
以线程锁为例:
@contextlib.contextmanager
def loudLock():
print 'Locking'
lock.acquire()
yield
print 'Releasing'
lock.release()
with loudLock():
print 'Lock is locked: %s' % lock.locked()
print 'Doing something that needs locking'
################Output:
#Locking
#Lock is locked: True
#Doing something that needs locking
#Releasing
4、contextlib.nested:减少嵌套
对于:
1
2
3
|
with
open
(filename,mode) as reader:
with
open
(filename1,mode1) as writer:
writer.write(reader.read())
|
可以通过contextlib.nested进行简化:
1
2
|
with contextlib.nested(
open
(filename,mode),
open
(filename1,mode1)) as (reader,writer):
writer.write(reader.read())
|
在python 2.7及以后,被一种新的语法取代:
1
2
|
with
open
(filename,mode) as reader,
open
(filename1,mode1) as writer:
writer.write(reader.read())
|
5、contextlib.closing()
file类直接支持上下文管理器API,但有些表示打开句柄的对象并不支持,如urllib.urlopen()返回的对象。还有些遗留类,使用close()方法而不支持上下文管理器API。为了确保关闭句柄,需要使用closing()为它创建一个上下文管理器(调用类的close方法)。
import contextlib class myclass(): def __init__(self): print '__init__' def close(self): print 'close()' with contextlib.closing(myclass()): print 'ok' 输出: __init__ ok close()
自定义线程池
简单版本:
import queue import threading import time class ThreadPool(object): def __init__(self, max_num=20): self.queue = queue.Queue(max_num) for i in range(max_num): self.queue.put(threading.Thread) def get_thread(self): return self.queue.get() def add_thread(self): self.queue.put(threading.Thread) ''' pool = ThreadPool(10) def func(arg, p): print(arg) time.sleep(1) p.add_thread() for i in range(30): Pool = pool.get_thread() t = Pool(target=func, args=(i, pool)) t.start() '''
复杂版本:
#!/usr/bin/env python # -*- coding:utf-8 -*- import queue import threading import contextlib import time StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None): if max_task_num: self.q = queue.Queue(max_task_num) else: self.q = queue.Queue() self.max_num = max_num self.cancel = False self.terminal = False self.generate_list = [] self.free_list = [] def run(self, func, args, callback=None): """ 线程池执行一个任务 :param func: 任务函数 :param args: 任务函数所需参数 :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数) :return: 如果线程池已经终止,则返回True否则None """ if self.cancel: return if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: self.generate_thread() w = (func, args, callback,)#主线程 self.q.put(w)#主线程 def generate_thread(self): """ 创建一个线程 """ t = threading.Thread(target=self.call) t.start() def call(self): """ 循环去获取任务函数并执行任务函数 """ current_thread = threading.currentThread() self.generate_list.append(current_thread) event = self.q.get()#if q为空,则阻塞住,一直等到有任务进来并把它取出来 while event != StopEvent: func, arguments, callback = event try: result = func(*arguments) success = True except Exception as e: success = False result = None if callback is not None: try: callback(success, result) except Exception as e: pass with self.worker_state(self.free_list, current_thread): if self.terminal: event = StopEvent else: event = self.q.get()#key:该线程在这里继续等待新的任务,任务来了,继续执行 #暂时将该线程对象放到free_list中。 else: self.generate_list.remove(current_thread) def close(self): """ 执行完所有的任务后,所有线程停止 """ self.cancel = True full_size = len(self.generate_list) while full_size: self.q.put(StopEvent) full_size -= 1 def terminate(self): """ 无论是否还有任务,终止线程 """ self.terminal = True while self.generate_list: self.q.put(StopEvent) self.q.queue.clear() @contextlib.contextmanager def worker_state(self, free_list, worker_thread): """ 用于记录线程中正在等待的线程数 """ free_list.append(worker_thread)#新的任务来的时候判断 # if len(self.free_list) == 0 and len(self.generate_list) < self.max_num # 任务得创建新的线程来处理;如果len(self.free_list) != 0:由阻塞着的存在free_list中的线程处理(event = self.q.get()) try: yield finally: free_list.remove(worker_thread) # How to use pool = ThreadPool(5) def callback(status, result): # status, execute action status # result, execute action return value pass def action(i): time.sleep(1) print(i) for i in range(30): ret = pool.run(action, (i,), callback) time.sleep(2) print(len(pool.generate_list), len(pool.free_list)) print(len(pool.generate_list), len(pool.free_list)) # pool.close() # pool.terminate()
延伸:
import contextlib import socket @contextlib.contextmanager def context_socket(host,port): sk=socket.socket() sk.bind((host,port)) sk.listen(5) try: yield sk finally:sk.close() with context_socket('127.0.0.1',8888) as socket: print(socket)