Pthon魔术方法(Magic Methods)-上下文管理

      Pthon魔术方法(Magic Methods)-上下文管理

                             作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.上下文管理方法

__enter__:
  进入与此对象相关的上下文。如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上。 __exit__:
  退出此对象相关的上下文。

二.案例展示

1>.上下文管理对象

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import time class Point:
def __init__(self):
print("1 Init start")
time.sleep(1)
print("2 Init over") def __enter__(self):
print("3 In enter") def __exit__(self, exc_type, exc_val, exc_tb):
print("6 Exit") p1 = Point() #实例化对象时并不会调用enter,而是先调用"__new__"实例化对象,在调用"__init__"方法进行初始化。 with p1 as p: #进入with语句块调用"__enter__"方法,然后执行语句体,最后离开with语句块的时候,调用"__exit__"方法。
"""
with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。
注意,with并不开启一个新的作用域。
""" print("4 In with")
time.sleep(2)
print("5 With over") print("7 ====End =====") #以上代码执行结果如下:
1 Init start
2 Init over
3 In enter
4 In with
5 With over
6 Exit
7 ====End =====

2>.上下文管理的安全性

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import time
import sys class Point:
def __init__(self):
print("1 Init start")
time.sleep(1)
print("2 Init over") def __enter__(self):
print("3 In enter") def __exit__(self, exc_type, exc_val, exc_tb):
print("6 Exit") p1 = Point() with p1 as p:
print("4 In with")
sys.exit(100) #尽管是退出Python运行环境,依然会执行"__exit__"函数,说明上下文管理很安全
time.sleep(2) print("5 With over") print("7 ====End =====") #以上代码执行结果如下:
1 Init start
2 Init over
3 In enter
4 In with
6 Exit

3>.with语句

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Point:
def __init__(self):
print("1 Init start")
print("2 Init over") def __enter__(self):
print("3 In enter")
return self #将实力本身返回 def __exit__(self, exc_type, exc_val, exc_tb):
print("6 Exit")
return True #如果返回的等价式为True则异常会被压制,若返回的等价式为Flase则异常会被继续抛出。 p1 = Point() with p1 as f: #with语法会调用with后的对象的"__enter__"方法,如果有as,则将该方法的返回值赋给as子句的变量
print("4 In with")
print(p1 == f) #此时我们可以说:f = p1.__enter__ 10 / 0 #尽管with语句中存在异常语句也会在退出with语句后调用"__exit__"方法
print("5 With over") print("7 ====End =====") #以上代码执行结果如下:
1 Init start
2 Init over
3 In enter
4 In with
True
6 Exit
7 ====End =====

4>.方法的参数

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Point:
def __init__(self):
print("1 Init start")
print("2 Init over") def __enter__(self):
print("3 In enter")
return self #将实力本身返回 def __exit__(self, exc_type, exc_val, exc_tb):
print("6 {}".format(exc_type)) #异常类型
print("7 {}".format(exc_val)) #异常的值
print("8 {}".format(exc_tb)) #异常的追踪信息
print("9 Exit")
return 10 > 8 #返回一个等效True的值,则压制异常;否则,继续抛出异常 p1 = Point() with p1 as f: #with语法会调用with后的对象的"__enter__"方法,如果有as,则将该方法的返回值赋给as子句的变量
print("4 In with")
print(p1 == f) #此时我们可以说:f = p1.__enter__ raise Exception("Error Info ...")
print("5 With over") print("10 ====End =====") #以上代码执行结果如下:
1 Init start
2 Init over
3 In enter
4 In with
True
6 <class 'Exception'>
7 Error Info ...
8 <traceback object at 0x000001952C6D8848>
9 Exit
10 ====End =====

5>.上下文应用场景

增强功能:  
  在代码执行的前后增加代码,以增强其功能,类似装饰器的功能。 资源管理:
  打开了资源需要关闭,例如文件对象,网络连接,数据库连接等。 权限验证:
  在执行代码之前,做权限的验证,在__enter__中处理。

6>.contextlib.contextmanager装饰器模拟上下文管理

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import contextlib
import datetime
import time """
contextlib.contextmanager装饰器:
它是一个装饰器实现上下文管理,装饰一个函数,而不用像类一样实现"__enter__"和"__exit__"方法。
对被装饰的函数有要求:
必须有yield,也就是这个函数必须返回一个生成器函数,且只有yield一个值,也就是这个装饰器接收一个生成器对象作为参数,这是为函数增加上下文管理的方式。 总结:
如果业务逻辑简单那可以使用函数加contextlib.contextmanager装饰器方式,如果业务复杂,用类的方式加"__enter__"和"__exit__"方法方便。
"""
@contextlib.contextmanager
def add(x,y):
start = datetime.datetime.now()
try:
print("1 in add ...")
time.sleep(2)
"""
把yield之前的代码当做上下文管理的"__enter__"方法执行
把yield之后的代码当做上下文管理的"__exit__"方法执行
把yield的作为"__enter__"的返回值
"""
yield x + y
print("3 out add ...")
finally:
delta = (datetime.datetime.now() - start).total_seconds()
print("函数执行所用时间为:{}秒".format(delta)) with add(10,20) as f:
print("2 {}".format(f)) #以上代码执行结果如下:
1 in add ...
2 30
3 out add ...
函数执行所用时间为:2.000312秒

三.小试牛刀

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import time
"""
为add函数添加代码执行时间功能:
方法一:
使用装饰器显示该函数的执行时长.
方法二:
使用上下文管理方法来显示该函数的执行时长.
""" def add(x,y):
time.sleep(2)
return x + y

1>.装饰器实现

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import time
import datetime
from functools import wraps def timeit(fn): @wraps(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
res = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print("{} 执行时间为: {}".format(fn.__name__,delta))
return res
return wrapper @timeit
def add(x,y):
time.sleep(2)
return x + y print(add(100,200)) #以上代码执行结果如下:
add 执行时间为: 2.000079
300

2>.上下文管理实现

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import time
import datetime
from functools import wraps,update_wrapper class TimeIt:
"""
This is TimeIt class
"""
def __init__(self,fn):
self.fn = fn
#把函数对象的文档字符串赋值给类
# self.__doc__ = fn.__doc__
# update_wrapper(self,fn)
wraps(fn)(self) def __enter__(self):
self.start = datetime.datetime.now()
return self.fn def __exit__(self, exc_type, exc_val, exc_tb):
self.delta = (datetime.datetime.now() - self.start).total_seconds()
print("{} 执行时间为: {}".format(self.fn.__name__,self.delta)) def __call__(self, *args, **kwargs): #该魔术方法就是把类当作装饰器用
self.start = datetime.datetime.now()
ret = self.fn(*args,**kwargs)
self.delta = (datetime.datetime.now() - self.start).total_seconds()
print("{} 执行时间为: {}".format(self.fn.__name__, self.delta))
return ret # @TimeIt
def add(x,y):
"""
This is add function.
"""
time.sleep(2)
return x + y with TimeIt(add) as fn:
print(fn(10,20)) print(add.__doc__)
print(TimeIt(add).__doc__) #以上代码执行结果如下:
30
add 执行时间为: 2.000407 This is add function. This is add function.
上一篇:Alpha冲刺——第十天


下一篇:浅谈PHP面向对象编程(六、自动加载及魔术方法)