目录
描述符
什么是描述符
描述符的作用
描述符初探
描述符实战
雏形
进阶1
进阶2 添加类型限制
逐一限制
终极boss 类的装饰器
描述符
什么是描述符
描述符本质是一个类,在设计上至少实现了__set__() __get__() __delete__() 其中之一,这也被称为描述符协议。
__set__():为一个属性赋值时触发
__get__():获取一个属性时触发
__delete__():用del删除一个属性时触发
描述符的作用
把描述符定义成另一个类的属性,前提是不能定义到构造函数中,此时描述符就可以代理另一个类的属性。所以注意了,由描述符本身实例化的对象进行获取、赋值、删除操作并不会触发这三个方法。
描述符初探
#描述符Str
class Str:
def __get__(self, instance, owner):
print(
'Str调用')
def __set__(self, instance, value):
print(
'Str设置...')
def __delete__(self, instance):
print(
'Str删除...')
#描述符Int
class Int:
def __get__(self, instance, owner):
print(
'Int调用')
def __set__(self, instance, value):
print(
'Int设置...')
def __delete__(self, instance):
print(
'Int删除...')
class People:
name=
Str()
age=
Int()
def __init__(self,name,age):
#name被Str类代理,age被Int类代理,
self.name=
name
self.age=
age
p1=People(
'alex',18
)
#输出结果
Str设置...
Int设置...
#描述符Str的使用
p1.name
p1.name=
'egon'
del p1.name
#输出结果
Str调用
Str设置...
Str删除..
#描述符Int的使用
p1.age
p1.age=18
del p1.age
#输出结果
Int调用
Int设置...
Int删除...
print(p1.
__dict__)
print(People.
__dict__)
# 输出结果可能让你费解
{}
{'__module__':
'__main__',
'name': <
__main__.Str object at 0x00000033945DEA58>,
'age': <
__main__.Int object at 0x00000033945DE2B0>,
'__init__': <function People.
__init__ at 0x0000003394753950>,
'__dict__': <attribute
'__dict__' of
'People' objects>,
'__weakref__': <attribute
'__weakref__' of
'People' objects>,
'__doc__': None}
#补充,看一下类型是否一致
print(type(p1) ==
People)
print(type(p1).
__dict__ == People.
__dict__)
True
True
View Code
从初始化到一系列的调用、赋值、删除,我们都不难理解触发,但是在输出__dict__的时候,我们发现实例化的对象内容为空,类对象的内容输出就很明确的看到了其中的两个属性是类对象。所以我们可以自己理解为被代理的类自身只知道有什么属性,但是是什么样子的不清楚了,在代理那里保存着呢!
描述符也有自身要遵守的优先级问题:
类属性
数据描述符(至少实现了__get__()和__set__())
实例属性
非数据描述符(没有实现__set__())
找不到的属性触发__getattr__()
在此,不对优先级再做验证,出现问题是可以考虑是不是这方面原因。
描述符实战
实现用描述符实现类型控制
雏形
class Str:
def __init__(self, name):
self.name =
name
def __get__(self, instance, owner):
print(
'get--->', instance, owner)
return instance.
__dict__[self.name]
def __set__(self, instance, value):
print(
'set--->', instance, value)
instance.__dict__[self.name] =
value
def __delete__(self, instance):
print(
'delete--->', instance)
instance.__dict__.pop(self.name)
class People:
name = Str(
'name')
def __init__(self, name, age, salary):
self.name =
name
self.age =
age
self.salary =
salary
p1 = People(
'egon', 18, 3231.3
)
# 调用
print(p1.
__dict__)
p1.name
print(p1.name)
# 赋值
print(p1.
__dict__)
p1.name =
'egonlin'
print(p1.
__dict__)
# 删除
print(p1.
__dict__)
del p1.name
print(p1.
__dict__)
set---> <
__main__.People object at 0x000000D9C28CFC18>
egon
{'name':
'egon',
'age': 18,
'salary': 3231.3
}
get---> <
__main__.People object at 0x000000D9C28CFC18> <
class '__main__.People'>
get---> <
__main__.People object at 0x000000D9C28CFC18> <
class '__main__.People'>
egon
{'name':
'egon',
'age': 18,
'salary': 3231.3
}
set---> <
__main__.People object at 0x000000D9C28CFC18>
egonlin
{'name':
'egonlin',
'age': 18,
'salary': 3231.3
}
{'name':
'egonlin',
'age': 18,
'salary': 3231.3
}
delete---> <
__main__.People object at 0x000000D9C28CFC18>
{'age': 18,
'salary': 3231.3}
View Code
进阶1
如果我在刚才代码里用类名调用会报错,说instance是空,所以需要修改get方法:
def __get__(self, instance, owner):
print(
'get--->',instance,owner)
if instance
is None:
return self
return instance.
__dict__[self.name]
View Code
再次调用的时候就不会再报错了 输出:get---> None <class '__main__.People'>
进阶2 添加类型限制
class Str:
def __init__(self,name,expected_type):
self.name=
name
self.expected_type=
expected_type
def __get__(self, instance, owner):
print(
'get--->',instance,owner)
if instance
is None:
return self
return instance.
__dict__[self.name]
def __set__(self, instance, value):
print(
'set--->',instance,value)
if not isinstance(value,self.expected_type):
#如果不是期望的类型,则抛出异常
raise TypeError(
'Expected %s' %
str(self.expected_type))
instance.__dict__[self.name]=
value
def __delete__(self, instance):
print(
'delete--->',instance)
instance.__dict__.pop(self.name)
class People:
name=Str(
'name',str)
#新增类型限制str
def __init__(self,name,age,salary):
self.name=
name
self.age=
age
self.salary=
salary
p1=People(123,18,3333.3)
#传入的name因不是字符串类型而抛出异常
set---> <
__main__.People object at 0x00000053639A4438> 123
Traceback (most recent call last):
File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 28,
in <module>
p1=People(123,18,3333.3)
#传入的name因不是字符串类型而抛出异常
File
"D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 24,
in __init__
self.name=
name
File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 14,
in __set__
raise TypeError(
'Expected %s' %
str(self.expected_type))
TypeError: Expected <
class 'str'>
View Code
逐一限制
class Typed:
def __init__(self,name,expected_type):
self.name=
name
self.expected_type=
expected_type
def __get__(self, instance, owner):
print(
'get--->',instance,owner)
if instance
is None:
return self
return instance.
__dict__[self.name]
def __set__(self, instance, value):
print(
'set--->',instance,value)
if not isinstance(value,self.expected_type):
raise TypeError(
'Expected %s' %
str(self.expected_type))
instance.__dict__[self.name]=
value
def __delete__(self, instance):
print(
'delete--->',instance)
instance.__dict__.pop(self.name)
class People:
name=Typed(
'name',str)
age=Typed(
'name',int)
salary=Typed(
'name',float)
def __init__(self,name,age,salary):
self.name=
name
self.age=
age
self.salary=salary
View Code
但是如果属性很多,是不是这样子就有点麻烦
终极boss 类的装饰器
class Typed:
def __init__(self,name,expected_type):
self.name=
name
self.expected_type=
expected_type
def __get__(self, instance, owner):
print(
'get--->',instance,owner)
if instance
is None:
return self
return instance.
__dict__[self.name]
def __set__(self, instance, value):
print(
'set--->',instance,value)
if not isinstance(value,self.expected_type):
raise TypeError(
'Expected %s' %
str(self.expected_type))
instance.__dict__[self.name]=
value
def __delete__(self, instance):
print(
'delete--->',instance)
instance.__dict__.pop(self.name)
def typeassert(**
kwargs):
def decorate(cls):
print(
'类的装饰器开始运行啦------>',kwargs)
for name,expected_type
in kwargs.items():
setattr(cls,name,Typed(name,expected_type))
return cls
return decorate
@typeassert(name=str,age=int,salary=float)
#有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
def __init__(self,name,age,salary):
self.name=
name
self.age=
age
self.salary=
salary
print(People.
__dict__)
p1=People(
'egon',18,3333.3
)
类的装饰器开始运行啦------> {
'name': <
class 'str'>,
'age': <
class 'int'>,
'salary': <
class 'float'>
}
{'__module__':
'__main__',
'__init__': <function People.
__init__ at 0x000000BCA4A43950>,
'__dict__': <attribute
'__dict__' of
'People' objects>,
'__weakref__': <attribute
'__weakref__' of
'People' objects>,
'__doc__': None,
'name': <
__main__.Typed object at 0x000000BCA48CEBA8>,
'age': <
__main__.Typed object at 0x000000BCA4A3FC18>,
'salary': <
__main__.Typed object at 0x000000BCA4A3FC88>
}
set---> <
__main__.People object at 0x000000BCA4A3FCF8>
egon
set---> <
__main__.People object at 0x000000BCA4A3FCF8> 18
set---> <
__main__.People object at 0x000000BCA4A3FCF8> 3333.3
View Code
转载于:https://www.cnblogs.com/yuliangkaiyue/p/9570566.html