我们不能选择自己的原生家庭,但我们可以选择自己今后成为什么样的人。
人生是一场马拉松,起步很重要,但可以抵达的终点更重要。
愿我们都有勇气,穿过最深沉的夜,突出重围,寻找到属于自己的人生意义!
总结
- with语法在打开实例时,并不都是实例本身;在enter方法中,return self 时才是实例本身;
- with语法在实例化的情况下,可以执行多次(复用),用完就释放;避免占用资源;
- 多个装饰器执行的顺序:(自下而上);
with语法在源码中大量使用,非常重要;
1. 上下文管理
什么是上下文管理器?
我们常见的with open操作文件,就是一个上下文管理器。如:
with open(file, 'rb') as f:
text = f.read()
上下文管理器:是指在一段代码执行之前执行一段代码,用于一些预处理工作entre;执行之后再执行一段代码,用于一些清理工作exit。
比如刚提到的文件操作,打开文件进行读写,读写完之后需要将文件关闭。很明显用到了上下文管理器。主要依靠enter、exit这两个”魔术方法”实现。
与方法的实现涉及到两个魔法函数_enter和_exit
class Contextor():
def __enter__(self):
print('程序的预处理开始啦!')
return self # 作为as说明符指定的变量的值
def __exit__(self, exc_type, exc_val, exc_tb):
print('正在进行收尾!')
def func(self):
print('程序进行中....')
with Contextor() as var:
var.func()
# 输出
程序的预处理开始啦!
程序进行中....
正在进行收尾!
从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在_enter中,而将资源的关闭写在_exit 中。
# 1.先enter ,最后执行exit;有raise异常都无所谓;
import time
class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
def __exit__(self,exc_type,exc_val,exc_tb):
print('exit')
with Point() as f:
print('~~~~~~~~~~~~~~~~') # 程序开始时 先 init,enter
time.sleep(1)
print('!!!!!!!!!!!!!!!!!') # 程序运行完成 时 执行 exit;
#---------------------------------------------------------------------------
init
enter
~~~~~~~~~~~~~~~~
!!!!!!!!!!!!!!!!!
exit
为什么文件对象和函数对象不相等?
# 2. 如果使用as 语法,其值为enter的返回值;
import time
class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self,exc_type,exc_val,exc_tb):
print('exit')
pt = Point()
with pt as f: # 如果使用as 语法,其值为enter的返回值;
print('~~~~~~~~~~~~~~~~') # 程序开始时 先 init,enter
print(f == pt)
f = open('test.ini')
with f as p: #
print(f == p,'~~~~~~~~~')
#----------------------------------------------------------
init
enter
~~~~~~~~~~~~~~~~
True
exit
True ~~~~~~~~~
exc_type,exc_val,exc_tb
在 写_exit_ 函数时,需要注意的事,它必须要有这三个参数:
•exc_type:异常类型
•exc_val:异常值
•exc_tb:异常的错误栈信息
当主逻辑代码没有报异常时,这三个参数将都为None。
# 3. 错误参数:exc_type,exc_val,exc_tb
import time
class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self,exc_type,exc_val,exc_tb):
print(exc_type) # 错误类型
print(exc_val) # 值
print(exc_tb) # 错误提示,追踪
print('exit')
pt = Point()
with pt as f: # 如果使用as 语法,其值为enter的返回值;
print('~~~~~~~~~~~~~~~~') # 程序开始时 先 init,enter
raise Exception('your error')
print(f)
#---------------------------------------------------------
init
enter
~~~~~~~~~~~~~~~~
<class 'Exception'>
your error
<traceback object at 0x000002DFBC0578C8>
exit
---------------------
exit 方法中 ;Return真值 会压制异常,不报错;
import time
class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
return self
def __exit__(self,exc_type,exc_val,exc_tb):
print(exc_type) # 错误类型
print(exc_val) # 值
print(exc_tb) # 错误提示,追踪
print('exit')
return True
pt = Point()
with pt as f: # 如果使用as 语法,其值为enter的返回值;
print('~~~~~~~~~~~~~~~~') # 程序开始时 先 init,enter
raise Exception('your error')
print(f)
#--------------------------------------------------------------------------
init
enter
~~~~~~~~~~~~~~~~
<class 'Exception'>
your error
<traceback object at 0x000002DFBC0E8408>
exit
练习:为加法函数计时
方法1、使用装饰器显示该函数的执行时长
方法2、使用上下文管理方法来显示该函数的执行时长
import time
def add(x, y):
time.sleep(2)
return x + y
装饰器实现
import time
import datetime
from functools import wraps
def timeit(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print('{} took {}s '.format(fn.__name__,delta))
return ret
return wrapper
@timeit
def add(x, y):
time.sleep(2)
return x + y
装饰器实现;
import time
import datetime
from functools import wraps
def timeit(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print('{} took {}s '.format(fn.__name__,delta))
return ret
return wrapper
@timeit
def add(x, y):
time.sleep(2)
return x + y
class Timeit:
def __init__(self, fn):
self.fn = fn
def __enter__(self):
self.start = datetime.datetime.now()
return self.fn
def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now() - self.start).total_seconds()
print("{} took {}s".format(self.fn.__name__, delta))
with Timeit(add) as fn:
#print(fn(4, 6))
print(add(4, 7))
# 类当做装饰器;
import time
import datetime
from functools import wraps
class Timeit:
def __init__(self, fn):
self.fn = fn
def __enter__(self):
self.start = datetime.datetime.now()
return self.fn
def __exit__(self, exc_type, exc_val, exc_tb):
delta = (datetime.datetime.now() - self.start).total_seconds()
print("{} took {}s".format(self.fn.__name__, delta))
def __call__(self, *args, **kwargs):
self.start = datetime.datetime.now()
ret = self.fn(*args, **kwargs)
delta = (datetime.datetime.now() - self.start).total_seconds()
print("{} took {}s. call".format(self.fn.__name__, delta))
return ret
@Timeit
def add(x,y):
"This is add function."
time.sleep(2)
return x+y
add(4,5)
print(add.__doc__)
print(Timeit(add).__doc__)
#------------------------------------------------------
add took 2.000628s. call
None
None
上面的类即可以用在上下文管理,又可以用做装饰器
上下文应用场景
- 增强功能
在代码执行的前后增加代码,以增强其功能。类似装饰器的功能。- 资源管理
打开了资源需要关闭,例如文件对象、网络连接、数据库连接等- 权限验证
在执行代码之前,做权限的验证,在 enter 中处理
2. contextlib.contextmanager
它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现 enter 和 exit 方法。
对下面的函数有要求,必须有yield,也就是这个函数必须返回一个生成器,且只有yield一个值。
也就是这个装饰器接收一个生成器对象作为参数。
import contextlib
@contextlib.contextmanager
def foo():
print('enter')
try:
yield # yield 5,yield的值只能有一个,作为__enter__方法的返回值
finally:
print('exit')
with foo() as f:
raise Exception()
print(f)
上例这么做有什么意义呢?
当yield发生处为生成器函数增加了上下文管理。这是为函数增加上下文机制的方式。
把yield之前的当做_enter方法执行
把yield之后的当做_exit方法执行
把yield的值作为_enter_的返回值
网友评论