描述符(property的原理)

描述符(descripto),用一句话来解释,描述符就是某种特殊的类的实例指派给另一个类的属性。那么什么是特殊类型的类呢?就是至少要在这个类中定义__get__()、__set__()、.__delete__()三个特殊方法中任意一个。

下面是描述符相关的魔法方法:

魔法方法 含义
__get__(self,instance,owner) 用于访问属性,它返回属性的值
__set__(self,instance,value) 将在属性分配操作中调用,不返回任何内容
__delete__(self,instance) 控制删除操作,不返回任何内容

举个例子

 class MyDescriptor():
def __get__(self,instance,owner):
print("getting...",self,instance,owner)
def __set__(self,instance,value):
print("setting...",self,instance,value)
def __delete__(self,instance):
print("deleting...",self,instance) class Test():
x = MyDescriptor()

由于MyDescriptor实现了__get__()、__set__()、__delete__()方法,并且将它的类实例指派给Test类的属性,所以MyDescriptor就是所谓描述符类。到这里,有没有看到property()的影子。

实例化Test类,然后尝试对x属性进行各种操作,看看描述符类会有什么

>>> class MyDescriptor():
def __get__(self,instance,owner):
print("getting...",self,instance,owner)
def __set__(self,instance,value):
print("setting...",self,instance,value)
def __delete__(self,instance):
print("deleting...",self,instance) >>> class Test():
x = MyDescriptor() >>> test = Test()
>>> test.x
getting... <__main__.MyDescriptor object at 0x000000000301BEF0> <__main__.Test object at 0x0000000002F6AB00> <class '__main__.Test'>

当访问x属性的时候,python会自动调用描述符的__get__()方法,几个参数的内容分别是,self是描述符类自身的实例;instance是这个描述符的拥着者所在的类的实例,在这个里也就是Test类的实例化;owner是这个描述符的拥有者所在的类本身。

>>> test.x = "X - abc"
setting... <__main__.MyDescriptor object at 0x000000000301BEF0> <__main__.Test object at 0x0000000002F6AB00> X - abc

对x属性进行赋值操作的时候,python会自动调用__set__()方法,前两个参数跟__get__()方法是一样的,最后一个参数value是等号右边的值。

最后一个del操作也是同样的道理:

>>> del test.x
deleting... <__main__.MyDescriptor object at 0x000000000301BEF0> <__main__.Test object at 0x0000000002F6AB00>

只要弄清楚描述符,那么property的秘密就不再是秘密了!property事实上就是一个描述符类。

class MyProperty:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
return self.fget(instance)
def __set__(self, instance, value):
self.fset(instance, value)
def __delete__(self, instance):
self.fdel(instance) class C:
def __init__(self):
self._x = None
def getX(self):
return self._x
def setX(self, value):
self._x = value
def delX(self):
del self._x x = MyProperty(getX, setX, delX)

下面说一个实例,先定义一个温度类,然后定义两个描述符类用于描述摄氏度和花摄氏度两个属性。两个属性会自动进行转换,也就是说,你可以给摄氏度这个属性赋值,然后打印花摄氏度属性是自动转换后的结果.

>>> class Celsius():
def __init__(self,value=26.0):
self.value = float(value)
def __get__(self,instance,owner):
return self.value
def __set__(self,instance,value):
instance.cel = float(value) >>> class Fahrenheit():
def __get__(self,instance,owner):
return instance.cel * 1.8 +
def __set__(self,instance,value):
instance.cel = (float(value)-)/1.8 >>> class Temperature():
cel = Celsius()
fan = Fahrenheit() >>> temp = Temperature()
>>> temp.cel
26.0
>>> temp.fan
78.80000000000001

定制序列

无规矩不成方圆,任何事物都有属于自己的规律,轴遵照属于自己的规律,事情才能朝向自己意向的方向发展。所以介绍定制容器,要想成功的实现容器定制,需要先说一下协议。协议(protocols)与其他编程语言中的接口很相似,它规定哪些地方必须要定义。然后,在python中的协议就显得不那么重要,事实上,在python中,协议更像是一种指南。

在python中,像序列类型(如列表、元组、字符串)或映射类型(如字典)都是属于容器类型。接下来说一下定制容器,那就必须要知道,定制容器有关的一些协议;

如果说你希望定制的容器是不可改变的话,你只要定义__len__()、__getitem__()方法。

如果你希望定制的容器是可变的话,除了__len__()、__getitem__()方法,还需要定义__setitem__()、__delitem__()两个方法。

下面列举了定制容器类型相关的魔法方法及含义。

魔法方法 含义
__len__(self) 定义被len()函数调用时的行为(返回容器中元素个数)
__getitem__(self,key) 定义获取容器中指定元素的行为,相当于self[key]
__setitem__(self) 定义设置容器中指定元素的行为,相当于self[key]
__delitem__(self) 定义删除容器中指定元素的行为,相当于del self[key]
__item__(self) 定义当迭代容器中的元素的行为
__reversed__(self) 定义当被reversed()函数调用时的行为
__contains__(self) 定义当使用成员测试运算符(in或not in)时的行为

是视乎展现你的技术了,可以动手编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。

 class CountList():
def __init__(self,*args):
self.values = [x for x in args]
self.count = {}.fromkeys(range(len(self.values)),0)
#这里使用列表的下标注作为字典的键,注意不能用元素作为字典的键
#因为列表的不同下标可能有值一样的元素,但字典不能有两个相同的键
def __len__(self):
return len(self.values)
def __getitem__(self,key):
self.count[key] += 1
return self.values[key] c1 = CountList(1,3,5,7,9)
c2 = CountList(2,4,6,8,10)
print(c1[1])
print(c2[1])
print(c1[1] + c2[1])
print(c1.count)
print(c2.count)

打印结果:

3
4
7
{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}

python_魔法方法(五):描述符和定制序列的更多相关文章

  1. Python笔记(二十五)_魔法方法_描述符

    描述符的属性方法 __get__(self, instance, owner): 用于访问属性,返回属性的值 __set__(self, instance, value): 用于给属性赋值时,返回属性 ...

  2. 课时46:魔法方法:描述符(property的原理)

    目录: 一.描述符(property的原理) 二.课时46课后习题及答案 ********************************** 一.描述符(property的原理) ********* ...

  3. 零基础学习python_魔法方法(41-48课)(迭代器)

    接下来这个为啥要叫魔法方法呢,额,这个嘛我是跟小甲鱼的视频取的名字一样的,因为会讲比较多杂的东西,有... 魔法方法详细阅读地址:http://bbs.fishc.com/thread-48793-1 ...

  4. 利用描述符自定制property

    利用描述符自定制property class Lazyproperty: def __init__(self,func): # print('==========>',func) self.fu ...

  5. Python之路(第二十七篇) 面向对象进阶:内置方法、描述符

    一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()( ...

  6. 【Python046--魔法方法:描述符】

    一.描述符的定义: 描述符就是将特殊类型的类的实例指派给另外一个类的属性 1.举例: 特殊类型的类要实现以下三个方法中的其中一个或者全部实现 * __get__(self,instance,owner ...

  7. python_魔法方法(三):__str__()和__repr__()

    使用python的魔法方法和time模块定制一个计时器的类 1.用time模块里的localtime()方法获取时间2.time.localtime返回struct_time格式3.表现你的类:__s ...

  8. python_魔法方法(二):算术运算

    python2.2之后,对类和类型做了同意,将int().float().str().list().touple()这些BIF转换为工厂函数 >>> type(len) <cl ...

  9. python 面向对象专题(十):特殊方法 (三)__get__、__set__、__delete__ 描述符(三)方法是描述符

    在类中定义的函数属于绑定方法(bound method),因为用户定义的函数都有 __get__ 方法,所以依附到类上时,就相当于描述符.示例 20-13 演示了从 面向对象专题(九)示例 20-8 ...

随机推荐

  1. jQuery DataTables 使用手册(精简版)

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/5182940.html 前排提醒,这个插件能不用就不用,那么多好的插件等着你,为什么要用它呢?就算用easyui的 ...

  2. css--offsetParent

    offsetParent属性返回一个对象的引用,这个对象是距离调用offsetParent的元素最近的(在包含层次中最靠近的),并且是已进行过CSS定位的容器元素. 如果这个容器元素未进行CSS定位, ...

  3. java多线程编程核心技术——第三章总结

    第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...

  4. 动态webService

    using System; using System.Net; using System.IO; using System.CodeDom; using Microsoft.CSharp; using ...

  5. shell入门-grep过滤-1

    正则表达式,就是一个字符串.有一定的规律.我们用指定的字符串匹配一个指定的行.指定的字符串就是正则表达式. 正则表达式有这几个工具:grep egrep sed awk 命令:gerep 说明:过滤出 ...

  6. 0009_if控制语句

    1.if 条件:                   (判断相等一定注意要用 ==  而不是 =) 代码块 else: 代码块 2.if 条件一: 代码块 elif 条件二: 代码块 elif 条件三 ...

  7. assert.ok()

    测试 value 是否为真值. 相当于 assert.equal(!!value, true, message). 如果 value 不为真值,则抛出一个带有 message 属性的 Assertio ...

  8. centos MAC 地址与报错eth0 unknown interface no such device

    eth0 unknown interface no such device 出现这个原因是由于虚拟机直接COPY过来,MAC地址发生了变化,但eth0 里仍然记录着旧的MAC地址. 解决方法: vim ...

  9. R: vector 向量的创建、操作等。

    ################################################### 问题:创建.操作向量   18.4.27 怎么创建向量 vector,,及其相关操作 ??? 解 ...

  10. 13、Actor

    1.介绍 Scala的Actor类似于Java中的多线程编程.但是不同的是,Scala的Actor提供的模型与多线程有所不同. Scala的Actor尽可能地避免锁和共享状态,从而避免多线程并发时出现 ...