python中关于with以及contextlib的使用

一般在coding时,经常使用with来打开文件进行文件处理,然后无需执行close方法进行文件关闭.

with open('test.py','r' as f:
	print(f.readline())

with的作用,类似于try...finally...,提供一种上下文机制.如果需要使用with语句的类,就必须内部提供了两个内置函数__enter__和__exit__,前者在主体代码前执行,后者在主体代码后执行.看下下面的例子吧.

class Tt:
    def output(self):
        print('hello,world!')

    def __enter__(self):
        print('enter!!!')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit!!!')
        if exc_type == ValueError:
            return True
        else:
            return False

with Tt() as t:
    t.output()
    print('do something~~')
print('==================')
with Tt() as e:
    raise ValueError('value error!')
print('==================')
with Tt() as e:
    raise Exception('can not detect! ')

out:

enter!!!
hello,world!
do something~~
exit!!!
==================
enter!!!
exit!!!
==================
enter!!!
Traceback (most recent call last):
exit!!!
  File "/Users/shane/PycharmProjects/Py_study/Base/S12/with_test.py", line 29, in <module>
    raise Exception('can not detect! ')
Exception: can not detect! 

是不是跟装饰器有点像?再来个contextlib吧!

contextlib是为了加强with语句,提供上下文机制的模块,它是通过Generator实现的。contextlib中的contextmanager作为装饰器来提供一种针对函数级别的上下文管理机制。下面看个例子吧:

import contextlib
free_list=[]
work_thread='alex'

@contextlib.contextmanager
def worker_state(state_list,work_thread):
    state_list.append(work_thread)
    try:
        print('go on!!')        #step1
        yield                   #step2
    finally:
        print(state_list)       #step5
        state_list.remove(work_thread)      #step6
        print(state_list)       #step7


with worker_state(free_list,work_thread):
    print(123)                  #step3
    print(456)                  #step4

out:

go on!!
123
456
['alex']
[]

contextlib.contextmanager 是python中内置的一个装饰器:执行with后面的函数时,先进入函数中执行,遇到yield时,跳出,执行下面的,最后执行函数中的finally

此种用法也可用来自动关闭socket

import contextlib,socket

@contextlib.contextmanager
def context_socket(host,port):
    sk=socket.socket()
    sk.bind((host,port))
    sk.listen(5)
    try:
        yield sk		#将sk返回,赋值给sock
    finally:
        sk.close()

with context_socket('127.0.0.1',8888) as sock:
    print(sock)
原文地址:https://www.cnblogs.com/ccorz/p/5702483.html