zoukankan      html  css  js  c++  java
  • Python中的描述器

    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
  • 相关阅读:
    Spring+Mybatis整合
    Spring入门之生命周期
    异常处理
    淘淘商城第一天
    Maven的Setting配置
    mysql下载
    整合mybatis的CRUD4
    整合mybatis的CRUD3
    整合mybatis的CRUD2
    整合mybatis的CRUD
  • 原文地址:https://www.cnblogs.com/wangchunli-blogs/p/9949863.html
Copyright © 2011-2022 走看看