• python 进阶:描述符


    为什么要使用描述符?
    
    假设你正在 写一个成绩管理系统,没有太多编码经验的你,可能会这样写:
    
    class Student:
        def __init__(self, name, math, chinese, english):
            self.name = name
            self.math = math
            self.chinese = chinese
            self.english = english
        def __repr__(self):
            return "<Student: {}, math:{}, chinese: {}, english:{}>".format(
            self.name, self.math, self.chinese, self.english )
    st1 = Student('小明', 88, 99, 35)
    st2 = Student('小红', 99, 78, 67)
    print(st1)
    print(st2)

    输出结果:

    <Student: 小明, math:88, chinese: 99, english:35>
    <Student: 小红, math:99, chinese: 78, english:67>

    这样看起来一切都很合理,但程序不像人那么智能,不会自动根据使用场景判断数据的合法性,如果老师输入成绩的时候,不小心将成绩录成了负数 或者超过成绩的最大值,程序是无法感知的。聪明的你马上想到了在代码中加入逻辑判断。

    class Student:
        def __init__(self, name, math, chinese, english):
            self.name = name
            if 0 <= math <= 100:
                self.math = math
            else:
                raise ValueError("Valid value must be in [0, 100]")
            if 0 <= chinese <= 100:
                self.chinese = chinese
            else:
                raise ValueError("Valid value must be in [0, 100]")
            if 0 <= english <= 100:
                self.english = english
            else:
                raise ValueError("Valid value must be in [0, 100]")
    
        def __repr__(self):
            return "<Student: {}, math:{}, chinese: {}, english:{}>".format(
                self.name, self.math, self.chinese, self.english)
    
    
    st1 = Student('小明', 88, 99, -35)
    st2 = Student('小红', 99, 78, 67)
    print(st1)
    print(st2)
    

      输出结果:

     这下程序稍微有点人工智能,能够自己明辨是非了。但是在__init__ 里有太多的逻辑判断,很影响代码的可读性。巧的是你学习了 Property 特性,可以很好的应用在这里。于是你的代码修改如下:

    class Student:
    
        def __init__(self, name, math, chinese, english):
            self.name = name
            self.math = math
            self.chinese = chinese
            self.english = english
    
        @property
        def math(self):
            return self._math
    
        @math.setter
        def math(self, value):
    
            if 0 <= value <= 100:
                self._math = value
            else:
                raise ValueError('Valid value must be in [0,100]')
    
        @property
        def chinese(self):
            return self._chinese
    
        @chinese.setter
        def chinese(self, value):
    
            if 0 <= value <= 100:
                self._chinese = value
            else:
                raise ValueError('Valid value must be in [0,100]')
    
        @property
        def english(self):
            return self._english
    
        @english.setter
        def english(self, value):
    
            if 0 <= value <= 100:
                self._english = value
            else:
                raise ValueError('Valid value must be in [0,100]')
    
        def __repr__(self):
            return "<Student:{},math:{},chinese:{},english:{}>". 
                format(self.name, self.math, self.chinese, self.english)
    

      代码的可读性瞬间提升了,功能上没啥问题,但是还是有点啰嗦,代码重复性太高,于是你开始研究更优雅的方式,接触到了描述符。什么是描述符? 一个实现了描述符协议的类就是一个描述符。

    什么是描述符协议? 实现了__get__(),__set__(),__delete__()其中至少一个方法的类,就是一个描述符。

    __get__():用于访问属性,他返回属性的值,若属性不存在,不合法等都可以抛出对应的异常

    __set__():将在属性分配操作中调用。不会返回任何内容

    __delete__():控制删除操作。不会返回任何内容。

    对描述符有了大概了解之后,你开始重写上面方法。

    from weakref import WeakKeyDictionary
    
    
    class Score():
        """ score should in [0,100]
        WeakKeyDictionary:弱引用字典,能防止内存泄漏
        """
    
        def __init__(self):
            self.score = WeakKeyDictionary()
            # self.score = {}
    
        def __get__(self, instance, owner):
            # print(instance,'__get__')
            return self.score[instance]
    
        def __set__(self, instance, value):
            if not isinstance(value, int):
                raise TypeError('Score must be int')
            if 0 <= value <= 100:
                self.score[instance] = value
            else:
                raise ValueError("score not in [0,100]")
    
    
    class Student():
        # 托管属性定义在类级别上
        math = Score()
        chinese = Score()
        english = Score()
    
        def __init__(self, name, math, chinese, english):
            self.name = name
            self.math = math
            self.chinese = chinese
            self.english = english
    
        def __repr__(self):
            return "<Student:{},math:{},chinese:{},english:{}>". 
                format(self.name, self.math, self.chinese, self.english)
    
    
    
    stu = Student("malong", 91, 77, 88)
    stu1 = Student("malong1", 57, 67, 78)
    print(stu)
    print(stu1)
    try:
        stu.english = -23
    except ValueError :
        stu.english=99
        print(stu)
    

     对比一下结果,发现确实优雅。

  • 相关阅读:
    并发队列 – 无界阻塞队列 LinkedBlockingQueue 原理探究
    并发队列 – 有界阻塞队列 ArrayBlockingQueue 原理探究
    Java回调机制解读
    一张图看懂encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的区别
    uva 111 History Grading
    hdu 2546 饭卡
    hdu 2602 Bone Collector
    uva 10720 Graph Construction
    uva 10716 Evil Straw Warts Live
    uva 10070 Camel trading
  • 原文地址:https://www.cnblogs.com/liangliangzz/p/14334998.html
Copyright © 2020-2023  润新知