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.

 

posted @ 2019-07-30 22:21  尹正杰  阅读(211)  评论(0编辑  收藏  举报