一、描述符的定义:
描述符就是将特殊类型的类的实例指派给另外一个类的属性
1、举例:
特殊类型的类要实现以下三个方法中的其中一个或者全部实现
* __get__(self,instance,owner)
--用于访问属性,它返回属性的值
* __set__(self,instance,value)
--用于设置属性,不返回任何内容
* __delete__(self,instance)
--控制删除操作,不返回任何内容
class MyDecriptor: def __get__(self,instance,owner): #打印查看描述符的get,set,delete方法中各个参数的含义 print("getting...",self,instance,owner) def __set__(self,instance,value): print("setting...",self,instance,value) def __delete__(self,instance): print("deleteing...",self,instance) class Test: #把特殊类MyDecriptor的实例指派给Test类的x属性 x = MyDecriptor() 执行结果: >>> test = Test() >>> test.x getting... <__main__.MyDecriptor object at 0x10a2186d8> <__main__.Test object at 0x10a208f60> <class '__main__.Test'> >>> test <__main__.Test object at 0x10a208f60> >>> Test <class '__main__.Test'> ''' |--由打印出来的getting...,可以看出来,参数self是MyDecriptor的对象(<__main__.MyDecriptor object at 0x10a2186d8>) |--参数instance是Test的对象(<__main__.Test object at 0x10a208f60>) |--参数owner是Test的本类自己(<class '__main__.Test'>) |--具体可由对象test打印出来的内容,和Test类打印出来的内容得到验证 |--set和delete同理 ''' >>> test.x = "X-man" setting... <__main__.MyDecriptor object at 0x10a2186d8> <__main__.Test object at 0x10a208f60> X-man >>> del test.x deleteing... <__main__.MyDecriptor object at 0x10a2186d8> <__main__.Test object at 0x10a208f60> >>>
2、为了能让描述符正常使用,必须定义在类的层次上,如果不是在类的层次上,Python无法会为你自动调用__get__(),__set__()方法
>>> class MyDes: def __init__(self, value = None): self.val = value def __get__(self, instance, owner): return self.val ** 2 >>> class Test: def __init__(self): self.x = MyDes(3) #用在了使用类的方法上,则不会调用描述符的__get__()和__set__()方法 >>> test = Test() >>> test.x <__main__.MyDes object at 0x109715588>
二、动手题
1、按要求编写描述符MyDes:当类的属性被访问,修改或设置的时候,分别作出提示
#当类的属性被访问,修改或设置时,分别给出提示 class MyDes: def __init__(self,initval=None,name=None): self.val = initval self.name = name def __get__(self,instance,owner): print("正在获取变量:",self.name) return self.val def __set__(self,instance,value): print("正在设置变量:",self.name) self.var = value def __delete__(self,instance): print("正在删除变量:",self.name) print("不能删除变量!") class Test: x = MyDes(10,'x') 执行结果: >>> test = Test() >>> test.x 正在获取变量: x 10 >>> test.x = 'X-man' 正在设置变量: x >>> del test.x 正在删除变量: x 不能删除变量! >>> test.x 正在获取变量: x 10
2、按要求编写描述符MyDes:记录指定变量的读取和写入操作,并将记录以及触发时间保存到文件:record.txt
考察的内容:字符串的读取,文件的操作,time模块的用法,描述符
import time as t class MyDes: def __init__(self,initval=None,name=None): self.var = initval self.name = name self.filename = "record.txt" def __get__(self,instance,owner): with open(self.filename,'a',encoding='utf-8') as f: f.write("%s变量于北京时间%s被读取,%s = %s " % (self.name,t.ctime(),self.name,str(self.var))) print("var==", self.var) return self.var def __set__(self, instance, value): filename = "%s_record.txt" % self.name with open(self.filename,'a',encoding='utf-8') as f: f.write("%s变量于北京时间%s被修改,%s = %s " %(self.name,t.ctime(),self.name,str(value))) print("value==",value) return value class Test: x=MyDes(10,'x') y=MyDes(8.8,'y') if __name__ == '__main__': for i in range(10): test = Test() print("get_testX++",test.x," get_testY--",test.y) test.x = 123 test.x = 1.23 test.y = "I LOVE AI" print(" ==================") 执行结果: var== 10 var== 8.8 get_testX++ 10 get_testY-- 8.8 value== 123 value== 1.23 value== I LOVE AI ================== var== 10 var== 8.8 get_testX++ 10 get_testY-- 8.8 value== 123 value== 1.23 value== I LOVE AI x变量于北京时间Wed Nov 14 23:32:54 2018被读取,x = 10 y变量于北京时间Wed Nov 14 23:32:54 2018被读取,y = 8.8 x变量于北京时间Wed Nov 14 23:32:54 2018被修改,x = 123 x变量于北京时间Wed Nov 14 23:32:54 2018被修改,x = 1.23 y变量于北京时间Wed Nov 14 23:32:54 2018被修改,y = I LOVE AI x变量于北京时间Wed Nov 14 23:32:54 2018被读取,x = 10 y变量于北京时间Wed Nov 14 23:32:54 2018被读取,y = 8.8 x变量于北京时间Wed Nov 14 23:32:54 2018被修改,x = 123 x变量于北京时间Wed Nov 14 23:32:54 2018被修改,x = 1.23 y变量于北京时间Wed Nov 14 23:32:54 2018被修改,y = I LOVE AI
3、编写描述符MyDes,使用文件来存储属性,属性的值会被存放到对应的pickle文件内(腌菜),如果属性被删除了,文件也会同时被删除,属性的名字也会被注销
# coding=utf-8 import os import pickle class MyDes: saved = [] def __init__(self,name= None): self.name = name self.filename = self.name + '.pkl' def __get__(self, instance, owner): if self.name not in MyDes.saved: raise AttributeError("%s 的属性没有被赋值!"%self.name) with open(self.filename,'rb') as f: value = pickle.load(f) print("value++ ",value) return value def __set__(self, instance, value): with open(self.filename,'wb') as f: pickle.dump(value,f) MyDes.saved.append(self.name) print("f_name ",MyDes.saved) with open(self.filename,'rb') as f: f_value = pickle.load(f) print("f_value-- ",f_value) def __delete__(self, instance): os.remove(self.filename) MyDes.saved.remove(self.name) class Test: x = MyDes('x') y = MyDes('y') if __name__ == '__main__': test = Test() test.x = '123' test.y = 'I LOVE AI'#del test.y 执行结果: f_name ['x'] f_value-- 123 f_name ['x', 'y'] f_value-- I LOVE AI value++ 123 value++ I LOVE AI 并且生成两个pkl文件:x.pkl y,pkl
扩展知识:
1、用于序列化的两个模块
json:用于字符串和Python数据类型间进行转换
pickle:用于Python特有的类型和Python的数据类型间进行转换
json提供四个功能:dumps,dump,loads,load
pickle提供四个功能:dumps,dump,loads,load
2、pickle可以存储什么类型的数据呢?
1)、所有python支持的原生类型:布尔型,整形,浮点型,复数,字符串,字节,None
2)、由任何原生类型组成的列表,数组,字典
3)、函数,类和类的实例
3、pickle中常用的方法有:
1)、pickle.dump(obj,file,protocol=none)
必填参数obj表示要封装的对象
必填参数file表示obj要写入的文件对象,file必须以二进制可写模式打开,即:wb
可选参数protocol表示告知pickler使用的协议,支持的协议有0,1,2,3 默认的协议是添加在Python3中的协议3
with open(self.filename,'wb') as f: pickle.dump(value,f) MyDes.saved.append(self.name) print("f_name ",MyDes.saved) #pickle.dump(obj,file)的使用必须结合open(file,'wb')
2)、 pickle.load(file,*,fix_imports=True, encoding="ASCII", errors="strict")
必填参数file必须已二进制可读模式打开,即:wb,其他都为可选参数
with open(self.filename,'rb') as f: f_value = pickle.load(f) print("f_value-- ",f_value) #pickle.load(file)的使用必须结合open(file,'rb')
3)、pickle.dumps(obj):已字节对象形式返回封装的对象,不需要写入文件中
4)、pickle.loads(bytes_object):从字节对象中读取被封装的对象,并返回
4、pickle模块可能出现三种异常:
1). PickleError:封装和拆封时出现的异常类,继承自Exception
2). PicklingError: 遇到不可封装的对象时出现的异常,继承自PickleError
3). UnPicklingError: 拆封对象过程中出现的异常,继承自PickleError