zoukankan      html  css  js  c++  java
  • python_魔法方法(五):描述符和定制序列

    描述符(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 + 32
        def __set__(self,instance,value):
            instance.cel = (float(value)-32)/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)时的行为

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

     1 class CountList():
     2     def __init__(self,*args):
     3         self.values = [x for x in args]
     4         self.count = {}.fromkeys(range(len(self.values)),0)
     5         #这里使用列表的下标注作为字典的键,注意不能用元素作为字典的键
     6         #因为列表的不同下标可能有值一样的元素,但字典不能有两个相同的键
     7     def __len__(self):
     8         return len(self.values)
     9     def __getitem__(self,key):
    10         self.count[key] += 1
    11         return self.values[key]
    12 
    13 c1 = CountList(1,3,5,7,9)
    14 c2 = CountList(2,4,6,8,10)
    15 print(c1[1])
    16 print(c2[1])
    17 print(c1[1] + c2[1])
    18 print(c1.count)
    19 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}
  • 相关阅读:
    Could not transfer artifact org.apache.maven.plugins:maven-resources-plugin:pom:2.6 from/to central
    SpringMVC详解
    数据库连接池
    事务的隔离级别
    数据库四大特性
    Eclipse自动编码提示设置
    RequestDispatcher.forward转发与HttpServletResponse.sendRedirect重定向
    c#代码混淆
    java反射机制
    (转)redis是什么
  • 原文地址:https://www.cnblogs.com/pinpin/p/9918321.html
Copyright © 2011-2022 走看看