python cookbook第三版学习笔记十三:类和对象(三)描述器

时间:2022-12-12 07:58:56
__get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性。读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法比较绕,我们来看一个实例:
class Descriptor(object):
    def __get__(self, instance, owner):
        return 'get',self,instance,owner class T(object):
    d=Descriptor()
if __name__ == "__main__":
    t=T()
    print t.d
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
('get', <__main__.Descriptor object at 0x017EB7D0>, <__main__.T object at 0x017EB7F0>, <class '__main__.T'>)
在这里可以看到Descriptor中实现了__get__方法。因此属于一个描述器。在T中引用这个描述器,在调用t.d的时候,实际上调用的__get__方法。__get__方法中instance是实例t,owner是对象T。self就是对象Descriptor
 
我们来看下书中的例子:
class integer(object):
    def __init__(self,name):
        self.name=name
    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]
    def __set__(self, instance, value):
        if not isinstance(value,int):
            raise TypeError('Expected an int')
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        del instance.__dict__[self.name] class Point(object):
    x=integer('x')
    y=integer('y')
    def __init__(self,value1,value2):
        self.x=value1
        self.y=value2
if __name__ == "__main__":
    p=Point(2,3)
    print p.x
这里首先在Point中定义了2个类属性变量,x和y,分别是对应的是interger对象。而interger实现了__set__以及__get__。因此属于描述器。对x,y和的调用将会用到__set__以及__get__方法。通过上面的可以看到在print p.x的时候实际上调用的是integer中的__get__方法。Instnace对应的是p。从这个实现可以看到当一个类变量被定义为描述器的时候,对这个类变量的操作将由描述器来进行代理。
 
这里介绍了描述器的用法,那么描述器用在哪些方面呢。描述器有什么用途呢。我们首先来看下这个例子:
 
如果我们在做一个学生的考试系统。
class student_score(object):
    def __init__(self,id,score,rating):
        self.id=id
        self.score=score
        self.rating=rating
    def get_result(self):
        return self.score*self.rating
if __name__ == "__main__":
    s=student_score(1,90,0.9)
    print s.get_result()
当我们在调用每一个student_score实例的时候,会传入每个学生的id,分数,等级。然后调用get_result得到分数。但是如果一下手误把分数或者等级输成负值比如s=student_score(1,-90,0.9)或者是s=student_score(1,90,-0.9)。这个学生的成绩就变成了个负值。这明显不合理啊。那么如何规避呢。代码改成如下:在__init__中进行保护
class student_score(object):
    def __init__(self,id,score,rating):
        self.id=id
        if score < 0 or rating <0:
            raise ValueError('parameter could not be negative')
        else:
            self.score=score
            self.rating=rating
    def get_result(self):
        return self.score*self.rating
if __name__ == "__main__":
    s=student_score(1,-90,0.9)
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
Traceback (most recent call last):
  File "E:/py_prj/python_cookbook/chapter8.py", line 156, in <module>
    s=student_score(1,-90,0.9)
  File "E:/py_prj/python_cookbook/chapter8.py", line 147, in __init__
    raise ValueError('parameter could not be negative')
ValueError: parameter could not be negative
这样在执行的时候,由于导入的是一个负值。因此报错。
但是如果我们在初始调用的时候是正确的,但是后续有人改变了这个参数呢。比如下面的代码。s.score=-90.直接将成绩修改了。
if __name__ == "__main__":
    s=student_score(1,90,0.9)
    s.score=-90
    print s.get_result()
这种在实例中修改的场景如何规避呢?一般会想到3种方法:
方法一:将score和rating设置为私有变量。通过get_score和set_score的方式来进行调用,然后在set_score中进行保护。代码如下。这种方法确实有效。但是如果我们要对多个变量进行保护,那不得对所有的变量都写个设置的函数。这样的代码的可读性就太差了
class student_score(object):
    def __init__(self,id,score,rating):
        self.id=id
        if score < 0 or rating <0:
            raise ValueError('parameter could not be negative')
        else:
            self.__score=score
            self.__rating=rating
    def get_result(self):
        return self.__score*self.__rating
    def get_score(self):
        return self.__score
    def set_score(self,value):
        if value < 0:
            raise ValueError('parameter could not be negative')
        else:
            self.__score=value
 
方法二:
用@property的方法。在这里
class student_score(object):
    def __init__(self,id,score,rating):
        self.id=id
        if score < 0 or rating <0:
            raise ValueError('parameter could not be negative')
        else:
            self._score=score
            self._rating=rating
    def get_result(self):
        return self._score*self._rating
    @property
    def score(self):
        return self._score
    @score.setter
    def score(self,value):
        if value < 0:
            raise ValueError('parameter could not be negative')
        else:
            self._score=value
if __name__ == "__main__":
s=student_score(1,90,0.9)
s.score=-91
使用了property之后,score函数可以当成属性值一样调用。并在调用s.score的时候将会调用score.setter进行值的判断。但是使用@property和方法一同样的面对一个问题就是如果需要对多个变量进行赋值保护。在需要些多个setter。这样的代码也很繁琐
方法3:
重写__setattr__。class student_score(object):

    def __init__(self,id,score,rating):
        self.id=id
        if score < 0 or rating <0:
            raise ValueError('parameter could not be negative')
        else:
            self.score=score
            self.rating=rating
    def get_result(self):
        return self.score*self.rating
    def __setattr__(self, key, value):
        if key == 'score' or key == 'rating':
            if value < 0:
                raise ValueError('parameter could not be negative')
            else:
                self.__dict__[key]=value if __name__ == "__main__":
    s=student_score(1,90,0.9)
    s.score=-91
在这种三种方法中。重写__setattr__是代码量最小的一种。在__setattr__对变量名进行判断。当变量名属于要判断的对象的时候。对值进行判断。这种用法比之前的两种方法要方便了很多。但是在__init__依然要进行判断,且还要重写__setattr__方法。如果有多个类,其中都有score或者rating要进行异常判断,哪所有的类都需要增加异常保护。有没有一种方法可以让我在实例中只进行过程处理,而用另外通过的方法进行异常保护?这里是描述器要解决的问题。
class score_descrptor(object):
    def __init__(self):
        pass
    def
__get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return instance.__dict__['score']
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError('parameter could not be negative')
        instance.__dict__['score']=value class student_score(object):
    score=score_descrptor()
    def __init__(self,id,score,rating):
        self.id=id
        if score < 0 or rating < 0:
            raise ValueError('parameter could not be negative')
        self.score=score
        self.rating=rating
    def get_result(self):
        return self.score*self.rating if __name__ == "__main__":
    s=student_score(1,90,0.9)
print s.score
    s.score=-91
上面的代码score_descrptor是一个描述器。当调用s.score的时候。执行的是score_descrptor.__get__。当调用s.score=-91的时候,执行的是score_descrptor.__set__. 通过这样的方法我们就将score的判断方法变成了一个通用的方法。只要任何类中需要判断score的正负的时候。都可以调用score_descrptor
 
来看一个描述器在延迟计算属性上的应用。我们希望将一个属性访问的时候计算结果,并且一旦被访问后,结果就被缓存起来,后续也可以继续调用,而不是每次都去计算
class lazyproperty(object):
    def __init__(self,func):
        self.func=func
    def __get__(self,instance,cls):
        if instance is None:
            return self
        else:
            value=self.func(instance)
            setattr(instance,self.func.__name__,value)
            return value class Circle(object):
    def __init__(self,radius):
        self.raidus=radius
    @lazyproperty
    def area(self):
        print 'computing area'
        return
math.pi*self.raidus**2 if __name__ == "__main__":
    c= Circle(4.0)
    print c.raidus
    print c.area
    print c.__dict__
1 首先在@lazyproperty的时候就等价在Circle 定义了area= lazyproperty(area)。并且将func赋值为area。在调用c.area将会跳到__get__去执行。
2 value=self.func(instance)传入Circle的实例,实际上执行的是area函数。
3 此时self.func.__name__=area, 通过setattr(instance,self.func.__name__,value)给Circle实例添加一个area的属性,并且这个属性的值是area函数的返回值
通过执行结果也可以看到在Cirlce的字典中也增加了area的属性。
E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py
4.0
computing area
50.2654824574
{'raidus': 4.0, 'area': 50.26548245743669}

python cookbook第三版学习笔记十三:类和对象(三)描述器的更多相关文章

  1. python cookbook第三版学习笔记十三:类和对象&lpar;四&rpar;描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  2. python cookbook第三版学习笔记十五:property和描述

    8.5 私有属性: 在python中,如果想将私有数据封装到类的实例上,有两种方法:1 单下划线.2 双下划线 1 单下划线一般认为是内部实现,但是如果想从外部访问的话也是可以的 2 双下划线是则无法 ...

  3. 《Linux命令、编辑器与shell编程》第三版 学习笔记---002

    <Linux命令.编辑器与shell编程>第三版 学习笔记---001 Linux命令.编辑器与shell编程 Shell准备 1.识别Shell类型 echo  $0 echo $BAS ...

  4. Java学习笔记之---类和对象

    Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态  例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...

  5. python cookbook第三版学习笔记十:类和对象&lpar;一&rpar;

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  6. Java学习笔记七 常用API对象三

    一.泛型:简单说就是对对象类型进行限定的技术 public class GenericDemo { public static void main(String[] args){ /*泛型作为1.5版 ...

  7. 学习笔记——Java类和对象

    今天学习了Java的类和对象的相关知识,由于Java面向对象的编程的思想和C++几乎一样,所以需要更多的关注Java的一些不同之处. 1.类 1.1 在类这一块,除了基本的成员变量,成员方法,构造函数 ...

  8. Java学习笔记 04 类和对象

    一.类和对象的概念 类 >>具有相同属性和行为的一类实体 对象 >>实物存在的实体.通常会将对象划分为两个部分,即静态部分和动态部分.静态部分指的是不能动的部分,被称为属性,任 ...

  9. python cookbook第三版学习笔记六:迭代器与生成器

    假如我们有一个列表 items=[1,2,3].我们要遍历这个列表我们会用下面的方式 For i in items:   Print i 首先介绍几个概念:容器,可迭代对象,迭代器 容器是一种存储数据 ...

随机推荐

  1. 抽象类 VS 接口

    引言 接口和抽象类是面向对象编程(OOP, Object Oriented programming)中两个绕不开的概念,二者相似而又有所不同.接下来,我们来了解二者的概念并比较它们的异同. 什么是抽象 ...

  2. java使用代理 html2canvas 截屏 将页面内容生成图片

    1.html2canvas 生成图片简单又好用,但涉及到跨域就会出现问题,官方给出的解决办法是设置代理.基本原理就是在后端将图片的数据生成base64再返回给前端使用.使canvas画布分析元素的时候 ...

  3. 数据库事务故障恢复undo日志检查点

      checkpoint 检查点 checkpoint,即检查点.在undolog中写入检查点,表示在checkpoint前的事务都已经完成commit或者rollback 了,也就是检查点前面的事务 ...

  4. C&num;按键打开文件选择对话框&comma;并把选择好的路径保存&sol;显示到textBox

    1.选择文件夹 FolderBrowserDialog fbd = new FolderBrowserDialog(); fbd.SelectedPath = "D:";//默认路 ...

  5. 利用HTML5&plus;Socket&period;io实现摇一摇控制PC端歌曲切换

    我比较喜欢听音乐,特别是周末的时候,电脑开着百度随心听fm,随机播放歌曲,躺在床上享受.但碰到了一个烦人的事情,想切掉不喜欢的曲子,还得起床去操作电脑换歌.于是思考能不能用手机控制电脑切换歌曲,经过一 ...

  6. Mr&period;Jobs

    Mr.Jobs   by xue 最近的情绪很down,情商智商一直往下降 主要的原因是工作不好找.不是我的要求太高, 而是公司的HR都很不要. 当然首先得自我检讨,但是,damn,这种事情我怎么做的 ...

  7. ibatis&period;net:尽可能的使用匿名类型替换 Hashtable

    一切尽在代码中 Hashtable 风格 public Account GetByCustomIdAndAccountType(int customId, AccountType accountTyp ...

  8. 迪杰斯特拉Dijkstra算法介绍

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijk ...

  9. C语言 &&num;183&semi; 三角形面积

     算法提高 三角形面积   时间限制:1.0s   内存限制:256.0MB      问题描述 由三角形的三边长,求其面积. 提示:由三角形的三边a,b,c求面积可以用如下的公式: s=(a+b+c ...

  10. java应用高cpu占用

    一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环 排查故障如下: 1.根据top命令,发现PID为28555的Java进程占用CPU高达200%,出现故障 2.通过ps ...