(转)面向对象(深入)|python描述器详解

原文:https://zhuanlan.zhihu.com/p/32764345

# 类似函数的形式
class A:
    def __init__(self, name, score):
        self.name = name # 普通属性
        self.score = score

    def getscore(self):
        return self._score

    def setscore(self, value):
        print(‘setting score here‘)
        if isinstance(value, int):
            self._score = value
        else:
            print(‘please input an int‘)

    score = property(getscore, setscore)

a = A(‘Bob‘,90)
a.name # ‘Bob‘
a.score # 90
a.score = ‘bob‘ # please input an int

分析上述调用score的过程

  • 初始化时即开始访问score,发现有两个选项,一个是属性,另一个是property(getscore, setscore)对象,因为后者中定义了__get____set__方法,因此是一个资料描述器,具有比属性更高的优先级,所以这里就访问了描述器
  • 因为初始化时是对属性进行设置,所以自动调用了描述器的__set__方法
  • __set__中对fset属性进行检查,这里即传入的setscore,不是None,所以调用了fsetsetscore方法,这就实现了设置属性时使用自定义函数进行检查的目的
  • __get__也是一样,查询score时,调用__get__方法,触发了getscore方法

下面是另一种使用property的方法

# 装饰器形式,即引言中的形式
class A:
    def __init__(self, name, score):
        self.name = name # 普通属性
        self.score = score

    @property
    def score(self):
        print(‘getting score here‘)
        return self._score

    @score.setter
    def score(self, value):
        print(‘setting score here‘)
        if isinstance(value, int):
            self._score = value
        else:
            print(‘please input an int‘)

a = A(‘Bob‘,90)
# a.name # ‘Bob‘
# a.score # 90
# a.score = ‘bob‘ # please input an int

下面进行分析

  • 在第一种使用方法中,是将函数作为传入property中,所以可以想到是否可以用装饰器来封装
  • get部分很简单,访问score时,加上装饰器变成访问property(score)这个描述器,这个score也作为fget参数传入__get__中指定调用时的操作
  • 而set部分就不行了,于是有了setter等方法的定义
  • 使用了propertysetter装饰器的两个方法的命名都还是score,一般同名的方法后面的会覆盖前面的,所以调用时调用的是后面的setter装饰器处理过的score,是以如果两个装饰器定义的位置调换,将无法进行属性赋值操作。
  • 而调用setter装饰器的score时,面临一个问题,装饰器score.setter是什么呢?是scoresetter方法,而score是什么呢,不是下面定义的这个score,因为那个score只相当于参数传入。自动向其他位置寻找有没有现成的score,发现了一个,是property修饰过的score,这是个描述器,根据property的定义,里面确实有一个setter方法,返回的是property类传入fset后的结果,还是一个描述器,这个描述器传入了fgetfset,这就是最新的score了,以后实例只要调用或修改score,使用的都是这个描述器
  • 如果还有del则装饰器中的score找到的是setter处理过的score,最新的score就会是三个函数都传入的score
  • 对最新的score的调用及赋值删除都跟前面一样了

property的原理就讲到这里,从它的定义我们可以知道它其实就是将我们设置的检查等函数传入get set等方法中,让我们可以自由对属性进行操作。它是一个框架,让我们可以方便传入其他操作,当很多对象都要进行相同操作的话,重复就是难免的。如果想要避免重复,只有自己写一个类似property的框架,这个框架不是传入我们希望的操作了,而是就把这些操作放在框架里面,这个框架因为只能实现一种操作而不具有普适性,但是却能大大减少当前问题代码重复问题

下面使用描述器定义了Checkint类之后,会发现A类简洁了非常多

class Checkint:

    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 isinstance(value, int):
            instance.__dict__[self.name] = value
        else:
            print(‘please input an integer‘)

# 类似函数的形式
class A:
    score = Checkint(‘score‘)
    age = Checkint(‘age‘)

    def __init__(self, name, score, age):
        self.name = name # 普通属性
        self.score = score
        self.age = age

a = A(‘Bob‘, 90, 30)
a.name # ‘Bob‘
a.score # 90
# a.score = ‘bob‘ # please input an int
# a.age=‘a‘ # please input an integer

描述器的应用

因为我本人也刚刚学描述器不久,对它的应用还不是非常了解,下面只列举我现在能想到的它有什么用,以后如果想到其他的再补充

  • 首先是上文提到的,它是实例方法、静态方法、类方法、property的实现原理
  • 当访问属性、赋值属性、删除属性,出现冗余操作,或者苦思无法找到答案时,可以求助于描述器
  • 具体使用1:缓存。比如调用一个类的方法要计算比较长的时间,这个结果还会被其他方法反复使用,我们不想每次使用和这个相关的函数都要把这个方法重新运行一遍,于是可以设计出第一次计算后将结果缓存下来,以后调用都使用存下来的结果。只要使用描述器在__get__方法中,在判断语句下,obj.__dict__[self.name] = value。这样每次再调用这个方法都会从这个字典中取得值,而不是重新运行这个方法。(例子来源最后的那个例子)

参考资料

参考网页如下

  • 官网的中文翻译,给出了描述器功能的整体框架及一些实例
  • 官网英文
  • 简书文章,主要讲解访问描述器顺序,静态方法、类方法和实例方法下的访问情况
  • 简书文章,对官网的@Property细节解读
  • 一篇译文可以再看看他下面附的参考资料(不要先看这篇,这篇有点深,而且个人认为他有些实现方法舍近求远)
  • 如果想看更多文章,搜索时注意:搜索“描述器”得到的文章高度重复,基本上就是上面几篇了,搜“描述符”会找到更多文章

原文地址:https://www.cnblogs.com/liujiacai/p/10946768.html

时间: 2024-08-24 10:25:47

(转)面向对象(深入)|python描述器详解的相关文章

Python装饰器详解,详细介绍它的应用场景

装饰器的应用场景 附加功能 数据的清理或添加: 函数参数类型验证 @require_ints 类似请求前拦截 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改 为函数提供额外的数据 mock.patch 函数注册 在任务中心注册一个任务 注册一个带信号处理器的函数 不同应用场景下装饰器实现 函数注册表 简单注册表 funcs = [] def register(func): funcs.append(func) return func @register def a(): r

python描述符详解

什么是描述符 官方的定义:描述符是一种具有"捆绑行为"的对象属性.访问(获取.设置和删除)它的属性时,实际是调用特殊的方法(_get_(),_set_(),_delete_()).也就是说,如果一个对象定义了这三种方法的任何一种,它就是一个描述符. 描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在被使用类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们可以通过查看实例和类的字典来确认这一点. 描述符是实现大部分Python类特性中最底层的数据结构的实现手

Python装饰器详解

装饰器简介: 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. 一个简单装饰器写法: 在写装饰器之前我们需要简单了解下python语法糖@的用法 http://www.cnblogs.com/Egbertbaron/p/72425

这是我见过最全面的Python装饰器详解!没有学不会这种说法!

上面是按下按钮1就存款,否则则取款. 不对,存取款要输入密码啊!!!所以,我们要加密码验证代码. 进群:125240963  即可获取数十套PDF哦! 可以看到,虽然实现了密码验证功能,但是代码冗余度比较高,而且现在只模拟了取款和存款功能,然而还有查询功能,转账功能等等,那么冗余度就更高了,而且相对于取款和存款函数来说,复用性没有那么高,所以我们要进一步优化代码,把验证函数写到取款和存款函数内部. 有没有什么方法,可以在不改变原函数以及原函数的调用的情况下扩展原函数的功能呢?当然是有的,这就是p

python装饰器详解,多层装饰器,及带参数的装饰器。

pyhon学习有一段时间了,今天又碰到了Django的中间件,其实Django里面的中间件,就是用了多层的装饰器,然后去了解了一下多层装饰器,感觉有写东西差不多快忘了,也可能前面没学好. 现在重新记录,学习下. 普通装饰器: def warp(func): print('我是装饰器,碰到需要装饰的函数,一开始执行这里') def inner(*args, **kwargs): print('这里才是真正的装饰开始!') res = func(*args, **kwargs) print('装饰结

python--装饰器详解

Python---装饰器详解 定义: 本质上是一个函数.作用是用来装饰另一个函数(即被装饰函数),给被装饰函数添加功能.前提是不能改变被装饰函数的源代码和调用方式.这样的一个函数称之为装饰器. 解析: 下面我们话不多说,直接用代码说明.下面是一个函数. 1 def add(): 2 b=1+2 3 print(b45 add() 程序输出: -------- 3 -------- 现在我要给这个函数增加一个解释性的句子,如下,我们可以编写一个装饰器: 1 #原函数 2 def add(): 3

Python描述器引导(转)

原文:http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html 1. Python描述器引导(翻译) 作者: Raymond Hettinger 联系: <python at rcn dot com> 翻译: hit9, iceout 译者注: 原文链接-http://docs.python.org/2/howto/descriptor.html Contents Python描述器引导(翻译) 摘要 定义和介绍 描述器

顶点着色器详解 (Vertex Shaders)

学习了顶点处理,你就知道固定功能流水线怎么将顶点从模型空间坐标系统转化到屏幕空间坐标系统.虽然固定功能流水线也可以通过设置渲染状态和参数来改变最终输出的结果,但是它的整体功能还是受限.当我们想实现一个外来的光照模型,外来的Fog或者点大小计算方式,等等,我们可能就放弃使用固定功能流水线,转而使用CPU来实现这些计算. 使用vertex shaders,它用一段小程序替换固定功能处理.这段小程序的输入是模型空间的顶点,输出齐次剪裁空间的顶点,并且还携带一些信息,如:per-vertex diffu

转载:唐磊的个人博客《python中decorator详解》【转注:深入浅出清晰明了】

转载请注明来源:唐磊的个人博客<python中decorator详解> 前面写python的AOP解决方案时提到了decorator,这篇文章就详细的来整理下python的装饰器--decorator. python中的函数即objects 一步一步来,先了解下python中的函数. def shout(word='hello,world'):     return word.capitalize() + '!'print shout()#输出:Hello,world!#跟其他对象一样,你同样