Pthon魔术方法(Magic Methods)-上下文管理
Pthon魔术方法(Magic Methods)-上下文管理
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.上下文管理方法
__enter__:
进入与此对象相关的上下文。如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上。
__exit__:
退出此对象相关的上下文。
二.案例展示
1>.上下文管理对象
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import time 7 8 class Point: 9 def __init__(self): 10 print("1 Init start") 11 time.sleep(1) 12 print("2 Init over") 13 14 def __enter__(self): 15 print("3 In enter") 16 17 def __exit__(self, exc_type, exc_val, exc_tb): 18 print("6 Exit") 19 20 21 p1 = Point() #实例化对象时并不会调用enter,而是先调用"__new__"实例化对象,在调用"__init__"方法进行初始化。 22 23 with p1 as p: #进入with语句块调用"__enter__"方法,然后执行语句体,最后离开with语句块的时候,调用"__exit__"方法。 24 """ 25 with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。 26 注意,with并不开启一个新的作用域。 27 """ 28 29 print("4 In with") 30 time.sleep(2) 31 print("5 With over") 32 33 print("7 ====End =====") 34 35 36 37 #以上代码执行结果如下: 38 1 Init start 39 2 Init over 40 3 In enter 41 4 In with 42 5 With over 43 6 Exit 44 7 ====End =====
2>.上下文管理的安全性
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import time 7 import sys 8 9 class Point: 10 def __init__(self): 11 print("1 Init start") 12 time.sleep(1) 13 print("2 Init over") 14 15 def __enter__(self): 16 print("3 In enter") 17 18 def __exit__(self, exc_type, exc_val, exc_tb): 19 print("6 Exit") 20 21 22 p1 = Point() 23 24 with p1 as p: 25 print("4 In with") 26 sys.exit(100) #尽管是退出Python运行环境,依然会执行"__exit__"函数,说明上下文管理很安全 27 time.sleep(2) 28 29 print("5 With over") 30 31 print("7 ====End =====") 32 33 34 35 #以上代码执行结果如下: 36 1 Init start 37 2 Init over 38 3 In enter 39 4 In with 40 6 Exit
3>.with语句
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 7 8 class Point: 9 def __init__(self): 10 print("1 Init start") 11 print("2 Init over") 12 13 def __enter__(self): 14 print("3 In enter") 15 return self #将实力本身返回 16 17 def __exit__(self, exc_type, exc_val, exc_tb): 18 print("6 Exit") 19 return True #如果返回的等价式为True则异常会被压制,若返回的等价式为Flase则异常会被继续抛出。 20 21 22 p1 = Point() 23 24 with p1 as f: #with语法会调用with后的对象的"__enter__"方法,如果有as,则将该方法的返回值赋给as子句的变量 25 print("4 In with") 26 print(p1 == f) #此时我们可以说:f = p1.__enter__ 27 28 10 / 0 #尽管with语句中存在异常语句也会在退出with语句后调用"__exit__"方法 29 print("5 With over") 30 31 print("7 ====End =====") 32 33 34 35 #以上代码执行结果如下: 36 1 Init start 37 2 Init over 38 3 In enter 39 4 In with 40 True 41 6 Exit 42 7 ====End =====
4>.方法的参数
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 7 8 class Point: 9 def __init__(self): 10 print("1 Init start") 11 print("2 Init over") 12 13 def __enter__(self): 14 print("3 In enter") 15 return self #将实力本身返回 16 17 def __exit__(self, exc_type, exc_val, exc_tb): 18 print("6 {}".format(exc_type)) #异常类型 19 print("7 {}".format(exc_val)) #异常的值 20 print("8 {}".format(exc_tb)) #异常的追踪信息 21 print("9 Exit") 22 return 10 > 8 #返回一个等效True的值,则压制异常;否则,继续抛出异常 23 24 25 p1 = Point() 26 27 with p1 as f: #with语法会调用with后的对象的"__enter__"方法,如果有as,则将该方法的返回值赋给as子句的变量 28 print("4 In with") 29 print(p1 == f) #此时我们可以说:f = p1.__enter__ 30 31 raise Exception("Error Info ...") 32 print("5 With over") 33 34 print("10 ====End =====") 35 36 37 38 #以上代码执行结果如下: 39 1 Init start 40 2 Init over 41 3 In enter 42 4 In with 43 True 44 6 <class 'Exception'> 45 7 Error Info ... 46 8 <traceback object at 0x000001952C6D8848> 47 9 Exit 48 10 ====End =====
5>.上下文应用场景
增强功能:
在代码执行的前后增加代码,以增强其功能,类似装饰器的功能。
资源管理:
打开了资源需要关闭,例如文件对象,网络连接,数据库连接等。
权限验证:
在执行代码之前,做权限的验证,在__enter__中处理。
6>.contextlib.contextmanager装饰器模拟上下文管理
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import contextlib 7 import datetime 8 import time 9 10 """ 11 contextlib.contextmanager装饰器: 12 它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现"__enter__"和"__exit__"方法。 13 对被装饰的函数有要求: 14 必须有yield,也就是这个函数必须返回一个生成器函数,且只有yield一个值,也就是这个装饰器接收一个生成器对象作为参数,这是为函数增加上下文管理的方式。 15 16 总结: 17 如果业务逻辑简单那可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加"__enter__"和"__exit__"方法方便。 18 """ 19 @contextlib.contextmanager 20 def add(x,y): 21 start = datetime.datetime.now() 22 try: 23 print("1 in add ...") 24 time.sleep(2) 25 """ 26 把yield之前的代码当做上下文管理的"__enter__"方法执行 27 把yield之后的代码当做上下文管理的"__exit__"方法执行 28 把yield的作为"__enter__"的返回值 29 """ 30 yield x + y 31 print("3 out add ...") 32 finally: 33 delta = (datetime.datetime.now() - start).total_seconds() 34 print("函数执行所用时间为:{}秒".format(delta)) 35 36 with add(10,20) as f: 37 print("2 {}".format(f)) 38 39 40 41 #以上代码执行结果如下: 42 1 in add ... 43 2 30 44 3 out add ... 45 函数执行所用时间为:2.000312秒
三.小试牛刀
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import time 7 """ 8 为add函数添加代码执行时间功能: 9 方法一: 10 使用装饰器显示该函数的执行时长. 11 方法二: 12 使用上下文管理方法来显示该函数的执行时长. 13 """ 14 15 def add(x,y): 16 time.sleep(2) 17 return x + y
1>.装饰器实现
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import time 7 import datetime 8 from functools import wraps 9 10 def timeit(fn): 11 12 @wraps(fn) 13 def wrapper(*args,**kwargs): 14 start = datetime.datetime.now() 15 res = fn(*args,**kwargs) 16 delta = (datetime.datetime.now() - start).total_seconds() 17 print("{} 执行时间为: {}".format(fn.__name__,delta)) 18 return res 19 return wrapper 20 21 @timeit 22 def add(x,y): 23 time.sleep(2) 24 return x + y 25 26 print(add(100,200)) 27 28 29 30 #以上代码执行结果如下: 31 add 执行时间为: 2.000079 32 300
2>.上下文管理实现
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import time 7 import datetime 8 from functools import wraps,update_wrapper 9 10 class TimeIt: 11 """ 12 This is TimeIt class 13 """ 14 def __init__(self,fn): 15 self.fn = fn 16 #把函数对象的文档字符串赋值给类 17 # self.__doc__ = fn.__doc__ 18 # update_wrapper(self,fn) 19 wraps(fn)(self) 20 21 def __enter__(self): 22 self.start = datetime.datetime.now() 23 return self.fn 24 25 def __exit__(self, exc_type, exc_val, exc_tb): 26 self.delta = (datetime.datetime.now() - self.start).total_seconds() 27 print("{} 执行时间为: {}".format(self.fn.__name__,self.delta)) 28 29 def __call__(self, *args, **kwargs): #该魔术方法就是把类当作装饰器用 30 self.start = datetime.datetime.now() 31 ret = self.fn(*args,**kwargs) 32 self.delta = (datetime.datetime.now() - self.start).total_seconds() 33 print("{} 执行时间为: {}".format(self.fn.__name__, self.delta)) 34 return ret 35 36 # @TimeIt 37 def add(x,y): 38 """ 39 This is add function. 40 """ 41 time.sleep(2) 42 return x + y 43 44 with TimeIt(add) as fn: 45 print(fn(10,20)) 46 47 print(add.__doc__) 48 print(TimeIt(add).__doc__) 49 50 51 52 #以上代码执行结果如下: 53 30 54 add 执行时间为: 2.000407 55 56 This is add function. 57 58 This is add function.
本文来自博客园,作者:尹正杰,转载请注明原文链接:https://www.cnblogs.com/yinzhengjie/p/11273187.html,个人微信: "JasonYin2020"(添加时请备注来源及意图备注,有偿付费)
当你的才华还撑不起你的野心的时候,你就应该静下心来学习。当你的能力还驾驭不了你的目标的时候,你就应该沉下心来历练。问问自己,想要怎样的人生。