描述符(__get__,__set__,__delete__)
1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
class Foo:
def __get__(self, instance, owner):
print('触发get')
def __set__(self, instance, value):
print('触发set')
def __delete__(self, instance):
print('触发delete')
#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#描述符Str
# 该描述符的作用是为另外一个类的类属性进行服务,另外一个类就是描述符的对象
class Str:
def __get__(self, instance, owner):
print('Str调用')
print(instance, owner)
def __set__(self, instance, value):
print('Str设置...')
print(instance,value)
def __delete__(self, instance):
print('Str删除...')
print(instance)
class People:
name = Str() # name 属性被代理,将这个类作用于另外一个类的属性来使用
def __init__(self,name): #name被Str类代理
self.name = name
p1 = People('long')
# 描述符Str的使用
p1.name
p1.name = 'sss'
del p1.name
print(p1.__dict__)
print(People.__dict__)
'''
结果:
Str设置...
<__main__.People object at 0x000001E6CA585C88> long
Str调用
<__main__.People object at 0x000001E6CA585C88> <class '__main__.People'>
Str设置...
<__main__.People object at 0x000001E6CA585C88> sss
Str删除...
<__main__.People object at 0x000001E6CA585C88>
{}
{'__module__': '__main__', 'name': <__main__.Str object at 0x000001E6CA585BE0>, '__init__': <function People.__init__ at 0x000001E6CA584048>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
'''
3 描述符分两种
一 数据描述符:至少实现了__get__()和__set__()
class Foo:
def __set__(self, instance, value):
print('set')
def __get__(self, instance, owner):
print('get')
二 非数据描述符:没有实现__set__()
class Foo:
def __get__(self, instance, owner):
print('get')
4 注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
1>类属性
2>数据描述符
3>实例属性
4>非数据描述符
5>找不到的属性触发__getattr__()
5 描述符使用
众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能
#描述符
class Typed:
def __init__(self,key):
self.key = key
def __get__(self, instance, owner):
print('get方法')
# print('instance参数:%s'%instance) # People object
# print('owner参数:%s'%owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
# 代理的好处:可以对传进来的值进行下一步判断
print('set方法')
# print('instance参数:%s'%instance) # People Object
# print('value参数:%s'%value)
if not isinstance(value,str):
raise TypeError('你传入的值不是字符类型')
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete方法')
# print('instance参数:%s'%instance) # People object
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('asd',18,330) # (数据描述符>实例)创建实例时,触发__set__
print(p1.__dict__)
print('-----------------')
p1.name = 'log'
print(p1.__dict__)
print('------------------')
name = p1.name
print(name)
print('------------------')
del p1.name
print(p1.__dict__)
'''
结果:
set方法
{'name': 'asd', 'age': 18, 'salary': 330}
-----------------
set方法
{'name': 'log', 'age': 18, 'salary': 330}
------------------
get方法
log
------------------
delete方法
{'age': 18, 'salary': 330}
'''
# -------------如果传入的name不是字符串类型-----------------
p2 = People(321,18,330)
'''
Traceback (most recent call last):
set方法
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 60, in <module>
p2 = People(321,18,330)
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 29, in __init__
self.name = name
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 18, in __set__
raise TypeError('你传入的值不是字符类型')
TypeError: 你传入的值不是字符类型
'''
#描述符
class Typed:
def __init__(self,key,key_type):
self.key = key
self.key_type = key_type
def __get__(self, instance, owner):
print('get方法')
# print('instance参数:%s'%instance) # People object
# print('owner参数:%s'%owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
# 代理的好处:可以对传进来的值进行下一步判断
print('set方法')
# print('instance参数:%s'%instance) # People Object
# print('value参数:%s'%value)
if not isinstance(value,self.key_type):
raise TypeError('%s不是%s'%(value,self.key_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete方法')
# print('instance参数:%s'%instance) # People object
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
# ----------------------3 多个值进行判断-------------------
p1 = People('long','18',2000)
'''
Traceback (most recent call last):
set方法
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 76, in <module>
set方法
p1 = People('long','18',2000)
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 32, in __init__
self.age = age
File "F:/Python3/老男孩/面向对象/day4/描述符应用.py", line 18, in __set__
raise TypeError('%s不是%s'%(value,self.key_type))
TypeError: 18不是<class 'int'>
'''