python之描述符应用与类的装饰器
一、描述符应用
1.1上下文管理协议
在上文中我们提到with语句中的上下文管理器。with语句可以如此简单但强大,主要依赖于上下文管理器。那么什么是上下文管理器?上下文管理器就是实现了上下文协议的类,而上下文协议就是一个类要实现__enter__()和__exit__()两个方法。一个类只要实现了__enter__()和__exit__(),我们就称之为上下文管理器下面我们具体说下这两个方法。
__enter__():主要执行一些环境准备工作,同时返回一资源对象。如果上下文管理器open("test.txt")的__enter__()函数返回一个文件对象。
__exit__():完整形式为__exit__(type, value, traceback),这三个参数和调用sys.exec_info()函数返回值是一样的,分别为异常类型、异常信息和堆栈。如果执行体语句没有引发异常,则这三个参数均被设为None。否则,它们将包含上下文的异常信息。__exit_()方法返回True或False,分别指示被引发的异常有没有被处理,如果返回False,引发的异常将会被传递出上下文。如果__exit__()函数内部引发了异常,则会覆盖掉执行体的中引发的异常。处理异常时,不需要重新抛出异常,只需要返回False,with语句会检测__exit__()返回False来处理异常。
如果我们要自定义一个上下文管理器,只需要定义一个类并且是实现__enter__()和__exit__()即可。
class Open: def __init__(self,name): self.name=name def __enter__(self): print('执行enter') return self def __exit__(self, exc_type, exc_val, exc_tb): print('执行exit') print(exc_type) print(exc_val) print(exc_tb) return True # exit里面吞噬了异常 with Open('a.txt') as f: #触发enter print(f) print(adcasd) #异常触发exit print(f.name) # 执行完后触发exit print('1234')
with的应用:
with obj as f: '代码块‘ 1 with obj ------>>触发obj.__enter__(),拿到返回值 2 as f -------->>f=返回值 3 with obj as f 等同于 f=obj.__enter__() 4 执行代码块 一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None 二:有异常的情况下,从异常的位置直接触发__exit__ a:如果的__exit__返回值为True,代表吞噬了异常 b:如果的__exit__返回值不为True,代表吐出了异常 c: __exit__的运行完毕就代表了整个with语句的执行完毕
1.2描述符的应用
描述符协议都是针对对象属性的访问。先要明白我们不会去针对一个全局的def使用property进行装饰。我们一般都是在类里面使用。可以对类的访问使用描述符(比较少用),更常用的是针对类实例的访问使用描述符协议。
class Typed: def __init__(self,key,expect_type): self.key=key self.expect_type=expect_type def __get__(self, instance, owner): print('执行get方法') return instance.__dict__[self.key] def __set__(self, instance, value): print('执行set方法') if not isinstance(value,self.expect_type): # print('传给的不是字符串,错误') # return raise TypeError('%s传给的不是%s' %(self.key,self.expect_type)) instance.__dict__[self.key]=value def __delete__(self, instance): print('执行delete方法') instance.__dict__.pop(self.key) class People: name=Typed('name',str) age=Typed('age',int) #age是数字 def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary # p1=People('czd',22,12345) p1=People('czd',22,12345) p1.age print(p1.age)