描述符

1.描述符本质就是一个新式类,在这个类中,至少实现了__set__,__get__,__delete__三种方法中的一种,也被称为描述符协议

  __get__:调用相关属性时触发

  __set__:修改相关属性时触发

  __delete__:删除相关属性时触发

2.至少实现了__get__,和__set__的描述符称为数据描述符

 不具有__set__方法的称为非数据描述符

3.属性调用优先级:类属性>数据描述符>实例属性>非数据描述符

4.描述符必须定义为类属性,不可放到函数中

 5.描述符是用来代理另一个类的属性的

class Foo:
    
    #self:描述符对象n,  instance:实例b1  owner:所有者类,b1所属的类Bar
    def __get__(self, instance, owner):
        print('===>get方法',self,instance,owner)

    def __set__(self, instance, value):
        print('===>set方法',self,instance,value)
        # instance.__dict__['n']=value

    def __delete__(self, instance):
        print('===>delete方法',instance)

class Bar:
    n=Foo()

    def __init__(self,name):
        self.name=name

# Bar.n=10            #类属性的设置优先级高于描述符,不触发__set__
# print(Bar.n)        #10

b1=Bar('alex')      #不涉及属性n时不会触发描述符
b1.n                #实例调用属性会触发__get__
#===>get方法 <__main__.Foo object at 0x00B3FFB0> <__main__.Bar object at 0x00B3F950> <class '__main__.Bar'>

#???为什么字典属性里并没有‘n'   : __set__方法并没有给字典添加
b1.n=10             #实例增加属性n,触发__set__,但类属性n不变
#===>set方法 <__main__.Foo object at 0x00B3FFB0> <__main__.Bar object at 0x00B3F950> 10
print(b1.__dict__)  #{'name': 'alex'}

print(b1.n)         #触发__get__
# ===>get方法 <__main__.Foo object at 0x02EDFFB0> <__main__.Bar object at 0x02EDF950> <class '__main__.Bar'>
# None

del b1.n            #触发__delete__
# ===>delete方法 <__main__.Bar object at 0x010EF950>

del Bar.n           #类删除属性也不会触发__delete__
View Code

 

class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')
    def __set__(self, instance, value):
        print('===>set方法',instance,value)
        instance.__dict__['x']=value
    def __delete__(self, instance):
        print('===>delete方法')


class Bar:
    x=Foo() #在何地?
    def __init__(self,n):
        self.x=n #b1.x=10
b1=Bar(10)
print(b1.__dict__)
b1.x=11111111111111111
print(b1.__dict__)

b1.y=11111111111111111111111111111111111111
print(b1.__dict__)

# result
# ===>set方法 <__main__.Bar object at 0x02E0F950> 10
# {'x': 10}
# ===>set方法 <__main__.Bar object at 0x02E0F950> 11111111111111111
# {'x': 11111111111111111}
# {'x': 11111111111111111, 'y': 11111111111111111111111111111111111111}
View Code

 

class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')
    def __set__(self, instance, value):
        print('===>set方法',instance,value)
        # instance.__dict__['x']=value #b1.__dict__
    def __delete__(self, instance):
        print('===>delete方法')


class Bar:
    x=Foo() #在何地?

print(Bar.x)        #类调用属性时触发__get__

Bar.x=1             #类设置属性时不会触发__set__
print(Bar.__dict__)
# {'__module__': '__main__', 'x': 1, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None}
print(Bar.x)        #1 ,这时就不会触发__get__


# b1=Bar()
# b1.x           #get
# b1.x=1        # set
# del b1.x      # delete


# b1=Bar()
# Bar.x=111111111111111111111111111111111111111
# b1.x            #不会触发__get__,类属性已经把描述符对象x覆盖了

# b1=Bar()
# del Bar.x       #不会触发__delete__
# b1.x            #报错:AttributeError: 'Bar' object has no attribute 'x'

#
#----------------------------------------------------

#实例属性>非数据描述符
class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')

    # def __delete__(self, instance):
    #     print('===>delete方法')


class Bar:
    x=Foo() #在何地?
    def  __getattr__(self, item):
        print('----->')
#
b1=Bar()
b1.x=1
print(b1.__dict__)          #{'x': 1}
b1.xxxxxxxxxxxxxxxxxxxxxxx  #----->
View Code

 

5.描述符的应用:

python是一门弱类型语言,即参数的赋值没有类型限制,下面通过描述符机制来实现类型限制功能

class Typed:
    def __get__(self, instance, owner):
        print('get方法',instance,owner,sep='----')

    def __set__(self, instance, value):
        print('set方法',instance,value,sep='----')

class People:
    name=Typed()

    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

#name被代理了,self.name=name触发的是__set__方法,与self.age,salary触发的方法不同
p1=People('alex',18,66.6)
print(p1.__dict__)          
p1.name
p1.name='sanxi'
print(p1.__dict__)

#result
# set方法----<__main__.People object at 0x00BCDAB0>----alex
# {'age': 18, 'salary': 66.6}
# get方法----<__main__.People object at 0x00BCDAB0>----<class '__main__.People'>
# set方法----<__main__.People object at 0x00BCDAB0>----sanxi
# {'age': 18, 'salary': 66.6}
为什么属性字典里没‘name’
class Typed:
    def __init__(self,key):
        self.key=key

    def __get__(self, instance, owner):
        print('get方法',instance,owner,sep='----')

    def __set__(self, instance, value):
        print('set方法',instance,value,sep='----')
        # instance.__dict__['name']=value     #写死了
        if not isinstance(value,str):
             raise TypeError('输入类型不是字符串')
        instance.__dict__[self.key]=value

    def __delete__(self, instance):
        print('delete方法',instance)
        instance.__dict__.pop(self.key)

class People:
    name=Typed('name')

    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary


p1=People('alex',18,66.6)           #set方法
# p1.name                             #get方法
# print(p1.__dict__)              #{'name': 'alex', 'age': 18, 'salary': 66.6}
# p1.name='sanxi'                     #set方法
# print(p1.__dict__)              #{'name': 'sanxi', 'age': 18, 'salary': 66.6}
#
# del p1.name                         #delete方法
# print(p1.__dict__)              #{'age': 18, 'salary': 66.6}

p1.name=123

#result:
# Traceback (most recent call last):
# set方法----<__main__.People object at 0x00FADAB0>----alex
# set方法----<__main__.People object at 0x00FADAB0>----123
#   File "D:/Programs/Python/Python37-32/untitled1/day21/time.py", line 37, in <module>
#     p1.name=123
#   File "D:/Programs/Python/Python37-32/untitled1/day21/time.py", line 12, in __set__
#     raise TypeError('输入类型不是字符串')
# TypeError: 输入类型不是字符串
实现了名字必须是字符串类型
class Typed:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self, instance, owner):
        print('get方法',instance,owner,sep='----')

    def __set__(self, instance, value):
        print('set方法',instance,value,sep='----')
        # instance.__dict__['name']=value     #写死了
        if not isinstance(value,self.expected_type):
             raise TypeError('输入类型不是%s'%self.expected_type)
        instance.__dict__[self.key]=value

    def __delete__(self, instance):
        print('delete方法',instance)
        instance.__dict__.pop(self.key)

class People:
    name=Typed('name',str)
    age=Typed('age',int)

    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary


p1=People('alex',18,66.6)           #set方法
#result
# set方法----<__main__.People object at 0x007E4130>----alex
# set方法----<__main__.People object at 0x007E4130>----18
对name,age都可以实现类型限制

 property:静态属性,                让你以为你是在调用一个数据属性,其实是在运行一个方法

class Lazyproperty:
    def __init__(self,func):
        print('==>',func)
        self.func=func 

    def __get__(self, instance, owner):
        return self.func(instance)


class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    # @property       #area=property(area)
    @Lazyproperty
    def area(self):
        return self.width*self.length

r1=Room('三夕',10,10)
print(r1.area)

#result:
# ==> <function Room.area at 0x00A13108>
# 100
利用描述符自定制property
class Lazyproperty:
    def __init__(self,func):
        print('==>',func)
        self.func=func

    def __get__(self, instance, owner):
        print(self,instance,owner,sep='---')
        if instance is None:
            return self
        return self.func(instance)


class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    # @property       #area=property(area)
    @Lazyproperty
    def area(self):
        return self.width*self.length


print(Room.area)

#result:

# ==> <function Room.area at 0x00F32108>
# <__main__.Lazyproperty object at 0x00E1F950>---None---<class '__main__.Room'>
# <__main__.Lazyproperty object at 0x00E1F950>
当类调用时返描述符对象----------与python自带的property  当类调用时一致

 

class Lazyproperty:
    def __init__(self,func):
        # print('==>',func)
        self.func=func

    def __get__(self, instance, owner):
        # print(self,instance,owner,sep='---')
        print('执行get方法')
        if instance is None:
            return self
        res=self.func(instance)
        setattr(instance,self.func.__name__,res)
        return res

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    # @property       #area=property(area)
    @Lazyproperty
    def area(self):
        return self.width*self.length

    @property
    def area1(self):
        return self.width * self.length

r1=Room('三夕',10,10)
print(r1.area)
print(r1.__dict__)
#实例属性> 非数据描述符,所以再次调用area属性时,会去找实例属性,而不触发__get__方法,从而延迟计算 
print(r1.area)

#result
# 执行get方法
# 100
# {'name': '三夕', 'width': 10, 'length': 10, 'area': 100}
# 100
自定义propert实现延迟计算功能

 

posted @ 2019-03-28 20:12  wind_y  阅读(231)  评论(0编辑  收藏  举报