zoukankan      html  css  js  c++  java
  • 【Python046--魔法方法:描述符】

    一、描述符的定义:

    描述符就是将特殊类型的类的实例指派给另外一个类的属性

    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

  • 相关阅读:
    又快又准的sql瓶颈诊断方法
    Qps从300到1500的优化过程
    Mysql性能优化全揭秘-庖丁解牛
    java学习笔记16-抽象类
    java学习笔记15-封装
    java学习笔记14-多态
    java学习笔记13-重写与重载
    Git学习笔记08-远程仓库
    Python3+Appium学习笔记09-元素定位android_uiautomator
    Python3+Appium学习笔记08-元素定位
  • 原文地址:https://www.cnblogs.com/frankruby/p/9951484.html
Copyright © 2011-2022 走看看