zoukankan      html  css  js  c++  java
  • 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.
  • 相关阅读:
    [转载]oracle中的exists 和not exists 用法详解
    oracle中sql语句的优化(转帖)
    BizTalk Server 2010 使用 WCF Service [ 上篇 ]
    冒泡排序
    一起复习几何(4)
    手把手教你升级到 Mysql 5.5
    BizTalk Server 2010 映射器(Mapper) [ 上篇 ]
    基于OpenGL的渲染引擎
    BizTalk Server 2010 映射器(Mapper) [ 下篇 ]
    BizTalk Server 2010 映射器(Mapper) [ 中篇 ]
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11273187.html
Copyright © 2011-2022 走看看