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. Netty(六)揭开 BootStrap 的神秘面纱

    6.1 客户端 BootStrap 6.1.1 Channel 简介 在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开)以及 ...

  2. nodejs非安装版配置(windows)

    nodejs官网下载地址: https://nodejs.org/en/download/ 解压到本地并配置环境变量 在环境变量path中新增 D:\develop\node 查看是否配置成功 至此n ...

  3. 高并发之Semaphore、Exchanger、LockSupport

    本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...

  4. 智能广告投放平台 All in One

    智能广告投放平台 All in One app demos 知之数据 一站式广告营销平台 https://hio.cn/ refs https://www.jonmellman.com/posts/p ...

  5. CSS Grid Layout In Action

    CSS Grid Layout In Action CSS 异形网格布局实战 refs https://static-nginx-online.fbcontent.cn/tutor/tutor-ybc ...

  6. React Hooks vs React Class vs React Function All In One

    React Hooks vs React Class vs React Function All In One React Component Types React Hooks Component ...

  7. learning-js-by-reading-source-codes

    learning-js-by-reading-source-codes BigInt https://github.com/learning-js-by-reading-source-codes/lo ...

  8. website 性能检测 & 前端性能优化

    website 性能检测 & 前端性能优化 https://cdn.xgqfrms.xyz/ https://mobile.xgqfrms.xyz/ PageSpeed Insights ht ...

  9. c++ string与wstring转换

    wchar_t to char #include <comdef.h> const wchar_t* exepath = L"d:\\中文 路径\\中文 路径.exe" ...

  10. CAD颜色对照表

    cad颜色代码与RGB参数对应表 1 255 0 02 255 255 03 0 255 04 0 255 2555 0 0 2556 255 0 2557 0 0 08 128 128 1289 1 ...