python——描述符

1、什么是描述符?

  python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。

2、讲解描述符前,先看一下属性:__dict__ (每个对象均具备该属性)

作用:字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}

对象属性的访问顺序:

①.实例属性

②.类属性

③.父类属性

④.__getattr__()方法

以上顺序,切记切记!

 1 class Test(object):
 2     cls_val = 1
 3     def __init__(self):
 4         self.ins_val = 10
 5
 6
 7 >>> t=Test()
 8 >>> Test.__dict__
 9 mappingproxy({‘__module__‘: ‘__main__‘, ‘cls_val‘: 1, ‘__init__‘: <function Test.__init__ at 0x0000000002E35D08>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Test‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Test‘ objects>, ‘__doc__‘: None})
10 >>> t.__dict__
11 {‘ins_val‘: 10}
12
13 >>> type(x)==X
14 True
15
16 #更改实例t的属性cls_val,只是新增了该属性,并不影响类Test的属性cls_val
17 >>> t.cls_val = 20
18 >>> t.__dict__
19 {‘ins_val‘: 10, ‘cls_val‘: 20}
20 >>> Test.__dict__
21 mappingproxy({‘__module__‘: ‘__main__‘, ‘cls_val‘: 1, ‘__init__‘: <function Test.__init__ at 0x0000000002E35D08>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Test‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Test‘ objects>, ‘__doc__‘: None})
22
23 #更改了类Test的属性cls_val的值,由于事先增加了实例t的cls_val属性,因此不会改变实例的cls_val值(井水不犯河水)
24 >>> Test.cls_val = 30
25 >>> t.__dict__
26 {‘ins_val‘: 10, ‘cls_val‘: 20}
27 >>> Test.__dict__
28 mappingproxy({‘__module__‘: ‘__main__‘, ‘cls_val‘: 30, ‘__init__‘: <function Test.__init__ at 0x0000000002E35D08>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Test‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Test‘ objects>, ‘__doc__‘: None})

从以上代码可以看出,实例t的属性并不包含cls_val,cls_val是属于类Test的。

 3、魔法方法:__get__(), __set__(), __delete__()

  方法的原型为:

  ① __get__(self, instance, owner)

  ② __set__(self, instance, value)

  ③ __del__(self, instance)

  那么以上的 self, instance owner到底指的是什么呢?

  首先我们先看一段代码:

 1 #代码 1
 2
 3 class Desc(object):
 4
 5     def __get__(self, instance, owner):
 6         print("__get__...")
 7         print("self : \t\t", self)
 8         print("instance : \t", instance)
 9         print("owner : \t", owner)
10         print(‘=‘*40, "\n")
11
12     def __set__(self, instance, value):
13         print(‘__set__...‘)
14         print("self : \t\t", self)
15         print("instance : \t", instance)
16         print("value : \t", value)
17         print(‘=‘*40, "\n")
18
19
20 class TestDesc(object):
21     x = Desc()
22
23 #以下为测试代码
24 t = TestDesc()
25 t.x
26
27 #以下为输出信息:
28
29 __get__...
30 self :          <__main__.Desc object at 0x0000000002B0B828>
31 instance :      <__main__.TestDesc object at 0x0000000002B0BA20>
32 owner :      <class ‘__main__.TestDesc‘>
33 ======================================== 

可以看到,实例化类TestDesc后,调用对象t访问其属性x,会自动调用类Desc的 __get__方法,由输出信息可以看出:

  ① self: Desc的实例对象,其实就是TestDesc的属性x

  ② instance: TestDesc的实例对象,其实就是t

  ③ owner: 即谁拥有这些东西,当然是 TestDesc这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的

到此,我可以揭开小小的谜底了,其实,Desc类就是是一个描述符(描述符是一个类哦),为啥呢?因为类Desc定义了方法 __get__, __set__.

所以,某个类,只要是内部定义了方法 __get__, __set__, __delete__ 中的一个或多个,就可以称为描述符- -

说到这里,我们的任务还远远没有完成,还存在很多很多的疑点。

  问题1. 为什么访问 t.x的时候,会直接去调用描述符的 __get__() 方法呢?

    答:t为实例,访问t.x时,根据常规顺序,

      首先:访问Owner的__getattribute__()方法(其实就是 TestDesc.__getattribute__()),访问实例属性,发现没有,然后去访问父类TestDesc,找到了!

      其次:判断属性 x 为一个描述符,此时,它就会做一些变动了,将 TestDesc.x 转化为 TestDesc.__dict__[‘x‘].__get__(None, TestDesc) 来访问

      然后:进入类Desc的 __get__()方法,进行相应的操作

  问题2. 从上面 代码1 我们看到了,描述符的对象 x 其实是类 TestDesc  的类属性,那么可不可以把它变成实例属性呢?

    答:直接看看解释器的结果。

 1 #代码 2
 2
 3 class Desc(object):
 4     def __init__(self, name):
 5         self.name = name
 6
 7     def __get__(self, instance, owner):
 8         print("__get__...")
 9         print(‘name = ‘,self.name)
10         print(‘=‘*40, "\n")
11
12 class TestDesc(object):
13     x = Desc(‘x‘)
14     def __init__(self):
15         self.y = Desc(‘y‘)
16
17 #以下为测试代码
18 t = TestDesc()
19 t.x
20 t.y
21
22 #以下为输出结果:
23 __get__...
24 name =  x
25 ======================================== 

    接下来的问题是,打印 t.y 的信息呢?

    因为没有访问 __get__() 方法。。。

    因为调用 t.y 时刻,首先会去调用TestDesc(即Owner)的 __getattribute__() 方法,该方法将 t.y 转化为TestDesc.__dict__[‘y‘].__get__(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,所以,只能忽略了。

  问题3. 如果 类属性的描述符对象 和 实例属性描述符的对象 同名时,咋整?

    答:还是让解释器来解释一下吧。

 1 #代码 3
 2
 3 class Desc(object):
 4     def __init__(self, name):
 5         self.name = name
 6         print("__init__(): name = ",self.name)
 7
 8     def __get__(self, instance, owner):
 9         print("__get__() ...")
10         return self.name
11
12     def __set__(self, instance, value):
13         self.value = value
14
15 class TestDesc(object):
16     _x = Desc(‘x‘)
17     def __init__(self, x):
18         self._x = x
19
20
21 #以下为测试代码
22 t = TestDesc(10)
23 t._x
24
25 #输入结果
26 __init__(): name =  x
27 __get__() ...

    不对啊,按照惯例,t._x 会去调用 __getattribute__() 方法,然后找到了 实例t 的 _x 属性就结束了,为啥还去调用了描述符的 __get__() 方法呢?

    这就牵扯到了一个查找顺序问题:当Python解释器发现实例对象的字典中,有与描述符同名的属性时,描述符优先,会覆盖掉实例属性。

    不信?来看一下 字典 :

1 >>> t.__dict__
2 {}
3
4 >>> TestDesc.__dict__
5 mappingproxy({‘__module__‘: ‘__main__‘, ‘_x‘: <__main__.Desc object at 0x0000000002B0BA20>, ‘__init__‘: <function TestDesc.__init__ at 0x0000000002BC59D8>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘TestDesc‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘TestDesc‘ objects>, ‘__doc__‘: None})

    怎么样,没骗你吧?我这人老好了,从来不骗人!

    我们再将 代码3 改进一下, 删除 __set__() 方法试试看会发生什么情况?

 1 #代码 4
 2
 3 class Desc(object):
 4     def __init__(self, name):
 5         self.name = name
 6         print("__init__(): name = ",self.name)
 7
 8     def __get__(self, instance, owner):
 9         print("__get__() ...")
10         return self.name
11
12 class TestDesc(object):
13     _x = Desc(‘x‘)
14     def __init__(self, x):
15         self._x = x
16
17
18 #以下为测试代码
19 t = TestDesc(10)
20 t._x
21
22 #以下为输出:
23 __init__(): name =  x

    我屮艸芔茻,咋回事啊?怎么木有去 调用 __get__() 方法?

    其实,还是 属性 查找优先级惹的祸,只是定义一个 __get__() 方法,为非数据描述符,优先级低于实力属性的!!

  问题4. 什么是数据描述符,什么是非数据描述符?

    答:一个类,如果只定义了 __get__() 方法,而没有定义 __set__(), __delete__() 方法,则认为是非数据描述符; 反之,则成为数据描述符

  问题5. 天天提属性查询优先级,就不能总结一下吗?

    答:好的好的,客官稍等!

    ① __getattribute__(), 无条件调用

    ② 数据描述符:由 ① 触发调用 (若人为的重载了该 __getattribute__() 方法,可能会调职无法调用描述符)

    ③ 实例对象的字典(若与描述符对象同名,会被覆盖哦)

    ④ 类的字典

    ⑤ 非数据描述符

    ⑥ 父类的字典

    ⑦ __getattr__() 方法

原文地址:https://www.cnblogs.com/huang-yc/p/9742350.html

时间: 2024-10-29 10:17:43

python——描述符的相关文章

杂项之python描述符协议

杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用,由于之前就看到过这一类的用法,但是一直没有明白具体是如何实现的,今天本着打破砂锅问到底的精神去网上搜资料,在这里不得不吐槽下百度搜索的垃圾了.....竞价排名做的那么6,搜一些技术文档.....各种坑爹...就是找不到想要的资源...于是FQ上google搜了搜,找到了python官网的文档...

python描述符descriptor(二)

python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): def __init__(self,getf,setf,delf,doc): self.getf=getf self.setf=setf self.delf=delf self.doc=doc def __set__(self,instance,own=None): if instance is N

转载python描述符介绍

来源:http://www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/ 简介 Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用.Python 描述符是一种创建托管属性的方法.除了其他优点外,托管属性还用于保护属性不受修改,或自动更新某个依赖属性的值. 描述符增加了对 Python 的理解,改善了编码技能.本文介绍了描述符协议,并演示了如何创建和使用描述符. 描述符协

python描述符descriptor(一)

Python 描述符是一种创建托管属性的方法.每当一个属性被查询时,一个动作就会发生.这个动作默认是get,set或者delete.不过,有时候某个应用可能会有 更多的需求,需要你设计一些更复杂的动作.最好的解决方案就是编写一个执行符合需求的动作的函数,然后指定它在属性被访问时运行.一个具有这种功能的对象 称为描述符.描述符是python方法,绑定方法,super,property,staticmethod和classmethod的实现基础. 1.描述符协议 描述符descriptor就是一个表

Iterator Protocol - Python 描述符协议

1 Iterator Protocol - Python 描述符协议 2 3 先看几个有关概念, 4 iterator 迭代器, 5 一个实现了无参数的 __next__ 方法, 并返回 '序列'中下一个元素,在没有更多的元素可返回的时候 raises StopIteration 的对象, 6 被称为 iterator(迭代器). 7 在 python 中, 迭代器 'iterator' 同时也实现了 __iter__ 方法, 故 iterators are iterable. 8 经典迭代器

Python描述符的使用

Python描述符的使用 前言 作为一位python的使用者,你可能使用python有一段时间了,但是对于python中的描述符却未必使用过,接下来是对描述符使用的介绍 场景介绍 为了引入描述符的使用,我们先设计一个非常简单的类: class Product(): def __init__(self,name,quantity,price): self.name = name self.quantity = quantity self.price = price 这是一个商品类,存储该商品的名称

Python描述符常用场景详解

Descriptors( 描述符 ) 是语言中一个深奥但很重要的一个黑魔法,它被广泛应用于 Python 语言的内核,熟练掌握描述符将会为 Python程序员 的工具箱添加一个额外的技巧.本文将讲述描述符的定义以及一些常见的场景,并且在文末会补充一下 __getattr , __getattribute__, __getitem__ 这三个同样涉及到属性访问的魔术方法,希望对大家 学习python有所帮助. 描述符的定义 descr__get__(self, obj, objtype=None)

Python描述符(descriptor)解密

Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些"中级"的语言特性有着完善的文档,并且易于学习. 但是这里有个例外,那就是描述符.至少对于我来说,描述符是Python语言核心中困扰我时间最长的一个特性.这里有几点原因如下: 有关描述符的官方文档相当难懂,而且没有包含优秀的示例告诉你为什么需要编写描述符(我得为Raymond Hettinger辩护一

python描述符

在Python中,访问一个属性的优先级顺序按照如下顺序:1.类属性2.数据描述符3.实例属性4.非数据描述符5.__getattr__()方法. 描述符,用一句话来说,就是将某种特殊类型的类的实例指派给另一个类的属性(注意:这里是类属性,而不是对象属性).而这种特殊类型的类就是实现了__get__,__set__,__delete__的新式类(即继承object). 阐述下这三个方法: __get__的标准定义是__get__(self,obj,type=None),第一个函数是调用它的实例,o