python快速学习系列(9):上下文管理器

上下文管理器context manager
-为什么要学context manager?
·类似于decorator,TensorFlow里面出现了不少context manager
·Pythonic的代码复用工具,适用于所有有始必有终模式的代码复用
·减少错误,降低编写代码的认知资源(比如打开文件需要关闭文件)
·提高代码可读性

-context manager与decorator之间的关系
·如果说decorator是对function和class的wrapper,那么context manager就是对任意形式的code block的wrapper

1.什么是context manager?与with语句是何关系?
context manager is a protocol for python with statement

执行时机:(双下划綫很不友好,不用代码表示会被认为是强调。。。)
·__init__():进入with语句时执行(optional,可实现也可不实现)
·__enter__():进入with代码块之前执行
·__exit__():离开with代码块之后执行
方法参数:
·__init__():context manager的初始化参数,自行定义
·__enter__():无参数
·__exit__():三个位置参数(type,instance,traceback)
说明:如果没有exception抛出,__exit__()的三个位置参数置为None

context manager通用结构:

class Foo:
    def __init__(self,stable=False):
        self.x = 0
        self.stable = stable
        print("__init__() called.")
    def __enter__(self):
        print("__enter__() called.")
        return self

    def __exit__(self,exc_type,exc_value,exc_traceback):
        print("__exit__() called.")
        if exc_type:
            print(f'exc_type:{exc_type}')
            print(f'exc_value:{exc_value}')
            print(f'exc_traceback:{exc_traceback}')
        if self.stable:#当stable=True时,即使出现异常也是返回True,也就是说抑制异常
        return True
        
    def add_one(self):
        self.x += 1
    def show_x(self):
        print(self.x)

def main():
    foo_cm_l = Foo()
    print('hello!')
    
with foo_cm_l as foo_cm:
    foo_cm.show_x()
    foo_cm.add_one()
    foo_cm.show_x()

print('hello')

with Foo() as foo_cm:
    foo_cm.show_x()
    foo_cm.add_one()
    foo_cm.show_x()

print('hello')

with foo_cm_l as foo_cm:
    foo_cm.show_x()
    foo_cm.add_one()
    foo_cm.show_x()

print('hello')

with Foo(True) as foo_cm:
    1 / 0

print('hello')

with foo_cm_l as foo_cm:
    1 / 0

if name == ‘main’:
main()

#以上代码的目的是为了看i)异常能否被抑制 ii)生命周期 结果如下

__init__() called.
hello!
__enter__() called.
0
1
__exit__() called.
hello
__init__() called.
__enter__() called.
0
1
__exit__() called.
hello
__enter__() called.
1
2
__exit__() called.
hello---
__init__() called.
__enter__() called.
__exit__() called.
exc_type:<class 'ZeroDivisionError'>
exc_value:division by zero
exc_traceback:<traceback object at 0x00000062D1918888>
hello----
__enter__() called.
__exit__() called.
exc_type:<class 'ZeroDivisionError'>
exc_value:division by zero
exc_traceback:<traceback object at 0x00000062D1918888>
Traceback (most recent call last):
  File "t.py", line 59, in <module>
    main()
  File "t.py", line 56, in main
    1 / 0
ZeroDivisionError: division by zero

2.context manager如何使用
·成对出现的模式——上下文管理器使用的信号:
-open-close
-setup-teardown
-lock-release
-change-reset
-enter-exit
-start-stop
-create-Delete

f = open() ---> f.close()
with open(path,mode) as f:
    f.read()
#这里就不用管f.close()了,因为在退出的时候,会自动调用

2)确保不同线程之间访问共享资源时的线程锁一定会释放

with threading.RLock():
    access_resource()
#同理,会自动释放

3)管理数据库的连接资源

conn = sqlite3.connect()
with conn:
    conn.execute("some SQL operations")
    conn.execute("some other SQL operations")
#会自动调用commit函数,断开连接等等

4)对某一块代码进行运行时间测量:

import time
class Timer:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        self.start = time.time()
    def __exit__(self,*args):
        self.end = time.time()
        self.interval = self.end - self.start
        print("%s took: %0.3f seconds" % (self.name,self.interval))
        return False
with Timer('fetching google homepage'):
    conn = httplib.HTTPConnection('google.com')
    conn.request('GET','/')

3.一个功能的代码实现与其工程化的区别
·某种或某些情况下可用vs任何情况下都可用
·资源足够的情况下可用vs有限的资源下可用
·自己可以运行起该程序vs任何人可自行运行
·自己可以继续开发修改vs任何人可继续开发修改
·强调功能本身vs强调:可用性、可扩展、性能、资源占用、安全、快速开发、容易交接、不易犯错

猜你喜欢

转载自blog.csdn.net/shanlepu6038/article/details/84671614