描述符
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,也被称为描述符协议,主要用户描述类属性,用在一个新式类调用的一个类的时候;
__get__():调用一个属性时触发;
__set__():为一个属性赋值时触发;
__delete__():采用del删除属性时触发;
作用
描述符的作用是用来代理另一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
示例(自己在调用的时候是不会执行的,只有在别的类调用的时候才会触发)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
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='cce'
print(f1.name)
del f1.name
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Foo:
def __get__(self, instance, owner):
print('get')
def __set__(self, instance, value):
print('set')
def __delete__(self, instance):
print('delete')
class Bar:
x=Foo()
y=1
b1=Bar()
b1.x
del b1.x
b1.x=2
# 结果 只有别的类在调用我们的类的时候才会触发描述符
# get
# delete
# set
描述符分为两种
1、数据描述符:至少实现了__get__()和__set__()
class Foo:
def __get__(self, instance, owner):
print('get')
def __set__(self, instance, value):
print('set')
2、非数据描述符:没有实现__set__()
class Foo:
def __get__(self, instance, owner):
print('get')
注意
1、描述符本身应该定义成新式类,被代理的类也应该是新式类;
2、必须把描述符定义成这个类的类属性,不能定义到构造函数中__init__;
3、严格遵守该优先级,优先级由高到低;
1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、找不到的属性触发__getattr__()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Foo:
def __get__(self, instance, owner):
print('get')
def __set__(self, instance, value):
print('set')
instance.__dict__['x']=value
def __delete__(self, instance):
print('delete')
class Bar:
x=Foo()
def __init__(self,name):
self.x=name
b1=Bar('cce')
print(b1.__dict__)
# 此处用文件描述符去处理x,一旦有关x的操作都由Foo代理
# set
# {'x': 'cce'}
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Foo:
def __get__(self, instance, owner):
print('====>get')
def __set__(self, instance, value):
print('====>set')
# instance.__dict__['x']=value
def __delete__(self, instance):
print('====>delete')
class Bar:
x=Foo()
Bar.x=1
print(Bar.x) # 此处打印的是1,说明当执行Bar.x=1的时候就给x重新赋值,此处也验证的类属性高于描述符的优先级,实际上就是底层字典的一个覆盖操作
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Foo:
def __get__(self, instance, owner):
print('====>get')
def __set__(self, instance, value):
print('====>set')
# instance.__dict__['x']=value
def __delete__(self, instance):
print('====>delete')
class Bar:
x=Foo()
b1=Bar()
b1.x=2 #此处触发的是====>set,由此可以看出实例属性的优先级低于描述符的优先级
print(b1.x) #此处触发的是====>get,由此可以看出实例属性的优先级低于描述符的优先级
实例属性和非数据描述符
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Foo:
def __get__(self, instance, owner):
print('====>get')
class Bar:
x=Foo()
b1=Bar()
b1.x =1
print(b1.__dict__) # 此处说明实例属性和非数据描述符实例属性的优先级更高
利用描述符限定用户输入的类型为字符串
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 利用描述符添加类型检测
class Typed:
def __init__(self,key):
self.key=key
def __get__(self, instance, owner):
print(instance.__dict__[self.key])
return '获取成功'
def __set__(self, instance, value):
if not isinstance(value,str):
print('输入错误,赋值不成功,仅接受字符串类型')
return
instance.__dict__[self.key]=value
print('赋值成功')
def __delete__(self, instance):
del instance.__dict__[self.key]
return '删除成功'
class People():
name=Typed('name')
def __init__(self,name,age):
self.name=name
self.age=age
p1=People('cce',18)
print(p1.__dict__)
p1.name=18
print(p1.__dict__)
# 执行结果
# 赋值成功
# {'name': 'cce', 'age': 18}
# 输入错误,赋值不成功,仅接受字符串类型
# {'name': 'cce', 'age': 18}
修订版
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
class Typed:
def __init__(self,key,type):
self.key=key
self.type=type
def __get__(self, instance, owner):
print(instance.__dict__[self.key])
return '获取成功'
def __set__(self, instance, value):
if not isinstance(value,self.type):
raise TypeError('输入错误,传入的内容不是【%s】' %self.type)
return
instance.__dict__[self.key]=value
print('赋值成功')
def __delete__(self, instance):
del instance.__dict__[self.key]
return '删除成功'
class People():
name=Typed('name',str)
age=Typed('age',int)
def __init__(self,name,age):
self.name=name
self.age=age
p1=People('cce',18)
print(p1.__dict__)
p1.name=18
p1.age='18'
print(p1.__dict__)
# 执行结果
# Traceback (most recent call last):
# File "C:/Users/Centos/Desktop/python/func.py", line 30, in <module>
# p1.name=18
# File "C:/Users/Centos/Desktop/python/func.py", line 13, in __set__
# raise TypeError('输入错误,传入的内容不是【%s】' %self.type)
# TypeError: 输入错误,传入的内容不是【<class>】/<class>
# 赋值成功
# 赋值成功
# {'name': 'cce', 'age': 18}
#!/usr/bin/env python
# -*- coding:utf-8 -*-
class Typed:
def __init__(self,key,type): # key 用来接受字典的key,也就是传入变量名。type用于接受用户传入该变量规定的类型
self.key=key
self.type=type
def __get__(self, instance,owner):
return instance.__dict__[self.key] # 当执行set操作的时候(包括实例化)会将该属性直接插入实例属性字典
def __set__(self, instance, value):
if isinstance(value,self.type): # 如果传入的类型是self.type的类型那么就插入实例字典
instance.__dict__[self.key]=value
else:
raise TypeError('类型错误') # 如果传入的类型不是self.type的类型那么就抛出TypeError
def __delete__(self, instance):
return instance.__dict__.pop(self.key) # 当执行del操作的时候,在实例属性字典中将这个属性给pop删除掉
class People:
name=Typed('name',str) # 表示name属性被描述符类代理
age=Typed('age',int) # 表示age属性被描述符类代理
def __init__(self,name,age):
self.name=name
self.age=age
p1=People('cce',18)
print(p1.__dict__)
閱讀更多 動漫資深愛好者和IT 的文章