• Python中的上下文管理器(contextlib模块)


    上下文管理器的任务是:代码块执行前准备,代码块执行后收拾

    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)
  • 相关阅读:
    静态化之优化
    SEO小技巧
    apache 工具和简单优化
    apache rewrite机制
    nginx php win平台配置
    mvc 简单模型
    php无限分类三种方式
    【转】sqlserver查询数据库中有多少个表
    【转】sqlserver数据库之间的表的复制
    SET ANSI_NULLS (TransactSQL)
  • 原文地址:https://www.cnblogs.com/Mengchangxin/p/9461239.html
Copyright © 2020-2023  润新知