21、描述器:Descriptors

1)描述器的表现

用到三个魔术方法。__get__()   __set__()  __delete__()

方法签名如下:

object.__get__(self,instance,owner)

object.__set__(self,instance,value)

object.__delete__(self,instance)

Self指指代当前实例,调用者。

Instance是owner的实例。

Owner是属性所属的类。

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



class B:

    x = A()

    def __init__(self):

        print('B init')



print(B.x.a1)  #A init   a1



b = B()

print(b.x.a1)   #      B init   a1
 

执行顺序是:第一个print执行类A的     第二个print执行的是类B。

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



    def __get__(self, instance, owner):

        print('{}{}{}'.format(self,instance,owner))



class B:

    x = A()

    def __init__(self):

        print('B init')



# print(B.x.a1)  #  会抛出错误,属性异常

#

# b = B()

# print(b.x.a1)   #会抛出错误,属性异常

出现错误的原因是:类A中定义get方法,那么类A就是描述器,报错的原因是和类A中的get方法的返回有关系。

get方法return返回值后:

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



    def __get__(self, instance, owner):

        print('{}{}{}'.format(self,instance,owner))

        return self



class B:

    x = A()

    def __init__(self):

        print('B init')



print(B.x.a1) 



b = B()

print(b.x.a1)  

A init

<__main__.A object at 0x00000090E02F2F60>None<class '__main__.B'>

a1

B init

<__main__.A object at 0x00000090E02F2F60><__main__.B object at 0x00000090E02F9518><class '__main__.B'>

a1

查看是否除了类属性以外,实例的属性是否可以触发get方法呢?

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



    def __get__(self, instance, owner):

        print('{}{}{}'.format(self,instance,owner))

        return self



class B:

    x = A()

    def __init__(self):

        print('B init')

        self.b = A()



print(B.x.a1) 

b = B()

print(b.x.a1)  

print(b.b)

总结:所以只有类属性是类的实例才可以。

2)描述器定义

描述器是一个类的类属性是另一个类的实例,另一个类中实现了set、delete和get方法之一。

有两个类A,B,类A中实现set和get的属性方法。类B中的一个属性为类A。

用一个类来增强另一个类的功能。

练习:模仿property描述器



class Property:

    def __init__(self,fget,fset = None):

        self.fget = fget

        self.fset = fset



    def __get__(self, instance, owner):

        if instance is not None:

            return self.fget(instance)

        return self



    def __set__(self, instance, value):

        print(self,instance,value)

        if callable(self.fset):

            self.fset(instance,value)

        else:

            raise AttributeError

    def setter(self,fn):

        self.fset = fn

        return self



class A:

    def __init__(self,data):

        self._data = data



    @Property     #data = Property(data)   #实例化

    def data(self):

        return self._data



    @data.setter      #data是Property的实例了    project.setter(data)

    def data(self,value):   #通过data.setter,提取参数,给fset. data等价于 = self

        self._data = value                 #data = Property(data)



a = A(100)

print(a.data)   #data>>值 <<函数(self)



a.data = 200

描述器定义:

描述器必须是类属性,Python中,一个类实现了__get__ 、__set__、__delete__三个任意一个方法都称为描述器。

仅仅实现__get__  非数据描述器。non-data descriptor

实现__get__  和__set__ 就是数据描述器。data descriptor

如果一个类的类属性设置为描述器,那么他被称为owner属主。

3)属性的访问顺序

非数据描述器,首先查找的是自己本身的字典。

数据描述器,首先查找的是类的字典。

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



    def __get__(self, instance, owner):

        print('{}{}{}'.format(self,instance,owner))

        return self



class B:

    x = A()

    def __init__(self):

        print('B init')

        self.x = 'b.x'



print(B.x.a1)

b = B()

print(b.x.a1)   #会抛出错误,属性异常

b.x访问到了实例的属性,而不是描述器。

修改代码,为类A增加__set__方法。

class A:

    def __init__(self):

        self.a1 = 'a1'

        print('A init')



    def __get__(self, instance, owner):

        print('{}{}{}'.format(self,instance,owner))

        return self



    def __set__(self, instance, value):

        print('{}{}{}'.format(self,instance,value))

        self.data = value



class B:

    x = A()

    def __init__(self):

        print('B init')

        self.x = 'b.x'



print(B.x.a1)

b = B()

print(b.x)

print(b.x.a1)  

A init

<__main__.A object at 0x0000006AD6EC9588>None<class '__main__.B'>

a1

B init

<__main__.A object at 0x0000006AD6EC9588><__main__.B object at 0x0000006AD6EC95F8>b.x

<__main__.A object at 0x0000006AD6EC9588><__main__.B object at 0x0000006AD6EC95F8><class '__main__.B'>

<__main__.A object at 0x0000006AD6EC9588>

<__main__.A object at 0x0000006AD6EC9588><__main__.B object at 0x0000006AD6EC95F8><class '__main__.B'>

a1

返回变成了a1,访问到了描述器的数据。

属性查找顺序:

实例的 __dict__ 优先于非数据描述器。

数据描述器优先于实例的 __dict__

4)本质(进阶)

不是因为数据描述器优先级高,而是把实例的属性从__dict__ 中去除掉了,数据访问的顺序还是按照原来的顺序执行。

5)Python中的描述器应用

描述器在Python中应用广泛,Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器,因此,实例重新定义和覆盖方法,允许单个实例获取与同一类的其他实例不同的行为。

****实现装饰器staticmethod和classmethod。

from functools import partial





class StaticMethod:

    def __init__(self,fn):

        self.fn = fn



    def __get__(self, instance, owner):

        return self.fn



class ClassMethod:

    def __init__(self,fn):

        self.fn = fn



    def __get__(self, instance, cls):

        return partial(self.fn,cls)



class A:

    @StaticMethod    #s_meth = StaticMethod(s_meth)

    def s_meth():

        print('static method')

    @ClassMethod

    def c_meth(cls):

        print('{}class method'.format(cls))

A.s_meth()

A.c_meth()

6)对实例的数据进行校验。

class Person:

    def __init__(self,name:str,age:int):

        params = ((name,str),(age,int))#利用二元组判断

        if not self.checkdata(params): #如果不为真,就抛出异常

            raise TypeError

        self.name = name

        self.age = age



    def checkdata(self,params):

        for p,t in params:

            if not isinstance(p,t):

                return False

        return True
(1)描述器方式:
class Check:

    def __init__(self,name,type):

        self.name = name

        self.type = type



    def __set__(self, instance, value):

        if not isinstance(value,self.type):#如果值和要求的类型不一致,则抛出异常,如果是则按照字典对应key和value值。

            raise TypeError(value)

        instance.__dict__[self.name] = value



    def __get__(self, instance, owner):

        if instance is not None:#如果对象不为空,返回的实例对象的字典名称对应的值,如果为空,则是返回实例本身。

            return instance.__dict__[self.name]

        return self





class Person:

    name = Check('name',str)

    age = Check('age',int)

    def __init__(self,name:str,age:int):

        self.name = name

        self.age = age





Person('tom',20)

Person('tom','20')
 

(2)利用装饰器

class Check:

    def __init__(self,name,type):

        self.name = name

        self.type = type



    def __set__(self, instance, value):

        if not isinstance(value,self.type):

            raise TypeError(value)

        instance.__dict__[self.name] = value



    def __get__(self, instance, owner):

        if instance is not None:

            return instance.__dict__[self.name]

        return self



import inspect

def typeassert(cls):

    params = inspect.signature(cls).parameters  #获取签名

    print(params)

    for name,param in params.items():

        print(param.name,param.annotation)

        if param.annotation != param.empty:#设置的属性不为空,检查是否和设置的属性一致。

            setattr(cls,name,Check(name,param.annotation))

    return cls



@typeassert

class Person:

    # name = Check('name',str)

    # age = Check('age',int)

    def __init__(self,name:str,age:int):

        self.name = name

        self.age = age



    def __repr__(self):

        return '{}is{}'.format(self.name,self.age)





p1 = Person('tom',20)

print(p1)

(3)封装成为类



class Check:

    def __init__(self,name,type):

        self.name = name

        self.type = type



    def __set__(self, instance, value):

        if not isinstance(value,self.type):

            raise TypeError(value)

        instance.__dict__[self.name] = value



    def __get__(self, instance, owner):

        if instance is not None:

            return instance.__dict__[self.name]

        return self



import inspect

class typeassert   #定义为一个类

# def typeassert(cls):

    def __init__(self,cls):

        self.cls = cls

        params = inspect.signature(self.cls).parameters

        print(params)

        for name,param in params.items():

            print(param.name,param.annotation)

            if param.annotation != param.empty:

                setattr(cls,name,Check(name,param.annotation))

        print(self.cls.__dict__)



    def __call__(self, name, age):   #构建可调用对象。

        p = self.cls(name,age)    #重新构建一个新的Person对象。

        return p





@typeassert

class Person:

    # name = Check('name',str)

    # age = Check('age',int)

    def __init__(self,name:str,age:int):

        self.name = name

        self.age = age



    def __repr__(self):

        return '{}is{}'.format(self.name,self.age)





p1 = Person('tom',20)

print(p1)

7)练习题,模仿property装饰器。

class Property:

    def __init__(self,fget,fset):

        self.fget = fset

        self.fset = fset



    def __get__(self, instance, owner):

        if instance in not None:

            return self.fget(instance)

        return self



    def __set__(self, instance, value):

        self.fset(instance,value)



    def setter(self,fn):

        self.fset = fn

        return self



class A:

    def __init__(self,data):

        self._data = data



    @Property   #data = Property(data)

    def data(self):

        return self._data



     @data.setter

    def data(self,value):

         self._data = value

Python中的描述器的更多相关文章

  1. python中的装饰器decorator

    python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...

  2. 简单说明Python中的装饰器的用法

    简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下   装饰器对与 ...

  3. 【Python】python中的装饰器——@

    对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...

  4. Python 中实现装饰器时使用 @functools.wraps 的理由

    Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过  ...

  5. 写python中的装饰器

    python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...

  6. python中@property装饰器的使用

    目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...

  7. python2.7高级编程 笔记二(Python中的描述符)

    Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...

  8. Python 面向对象(五) 描述器

    使用到了__get__,__set__,__delete__中的任何一种方法的类就是描述器 描述器的定义 一个类实现了__get__,__set__,__delete__中任意一个,这个类就是描述器. ...

  9. python类:描述器Descriptors和元类MetaClasses

    http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...

随机推荐

  1. 鸟哥的linux私房菜——第九章学习(vim编辑器)

    第九章vim编辑器 1.0).vi与vim Linux下文本界面的文书编辑器通常会有常常听到的就有: emacs, pico, nano, joe, 与 vim 等等. vi的优势: 所有的 Unix ...

  2. Leetcode(22)-括号生成

    给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", "(()())& ...

  3. Invalid prop: custom validator check failed for prop "***"

    使用element ui时,绑定参数参考官方参数说明,比如progress的status 值必须是在以下三个里面选择 "success/exception/text", 其它情况可 ...

  4. 问题记录 java.lang.NoClassDefFoundError: org/dom4j/DocumentException

    客户端调webservice服务产生以下错误 AxisFault faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.genera ...

  5. Media Queries语法总结

    Media Queries的语法如下所示: @media [media_query] media_type and media_feature 使用Media Queries样式模块时都必须以&quo ...

  6. Seven xxx in Seven Weeks ebooks | 七周七 xxx 系列图书 电子书| share 分享 | free of charge 免费!

    Seven xxx  in Seven Weeks ebooks |  七周七 xxx 系列图书  电子书| share  分享 | free of charge  免费! Seven Languag ...

  7. javascript change array length methods

    javascript change array length methods Array 改变数组长度的方法 push, pop shift, unshift, splice, fill, 不改变数组 ...

  8. modal over table bug

    modal over table bug table can not clickable bad <el-row> <el-col :span="24"> ...

  9. 1004 Counting Leaves ——PAT甲级真题

    1004 Counting Leaves A family hierarchy is usually presented by a pedigree tree. Your job is to coun ...

  10. 1107 Social Clusters——PAT甲级真题

    1107 Social Clusters When register on a social network, you are always asked to specify your hobbies ...